diff --git a/apps/admin-x-settings/src/components/settings/general/users/profile-tab.tsx b/apps/admin-x-settings/src/components/settings/general/users/profile-tab.tsx index d08805cdfa9..6c2ffcda510 100644 --- a/apps/admin-x-settings/src/components/settings/general/users/profile-tab.tsx +++ b/apps/admin-x-settings/src/components/settings/general/users/profile-tab.tsx @@ -3,11 +3,13 @@ import RoleSelector from './role-selector'; import StaffToken from './staff-token'; import {SettingGroup, SettingGroupContent, TextArea, TextField} from '@tryghost/admin-x-design-system'; import {type UserDetailProps} from '../user-detail-modal'; +import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site'; import {hasAdminAccess} from '@tryghost/admin-x-framework/api/users'; import {useGlobalData} from '../../../providers/global-data-provider'; const BasicInputs: React.FC = ({errors, clearError, user, setUserData}) => { - const {currentUser} = useGlobalData(); + const {currentUser, siteData} = useGlobalData(); + const homepageUrl = getHomepageUrl(siteData!); return ( @@ -36,7 +38,7 @@ const BasicInputs: React.FC = ({errors, clearError, user, setUs onKeyDown={() => clearError('name')} /> { await modal.getByLabel('Full name').fill('New Admin'); await modal.getByLabel('Email').fill('newadmin@test.com'); await modal.getByLabel('Slug').fill('newadmin'); - await expect(modal.getByText('https://example.com/author/newadmin')).toBeVisible(); + await expect(modal.getByText('http://test.com/author/newadmin')).toBeVisible(); await modal.getByLabel('Location').fill('some location'); await modal.getByLabel('Bio').fill('some bio'); diff --git a/ghost/admin/package.json b/ghost/admin/package.json index c7742ce961e..c81d40d9f79 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "6.19.0", + "version": "6.19.1", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", diff --git a/ghost/core/package.json b/ghost/core/package.json index 7369bffd63f..0aa8b874574 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "6.19.0", + "version": "6.19.1", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org", @@ -330,19 +330,19 @@ "test:browser": { "dependsOn": [ "build:assets", - "ghost-admin:build" + "@tryghost/admin:build" ] }, "test:browser:admin": { "dependsOn": [ "build:assets", - "ghost-admin:build" + "@tryghost/admin:build" ] }, "test:browser:portal": { "dependsOn": [ "build:assets", - "ghost-admin:build" + "@tryghost/admin:build" ] } } diff --git a/ghost/core/test/e2e-api/admin/members-exporter.test.js b/ghost/core/test/e2e-api/admin/members-exporter.test.js index 125b3741e2e..1dc1d078ad7 100644 --- a/ghost/core/test/e2e-api/admin/members-exporter.test.js +++ b/ghost/core/test/e2e-api/admin/members-exporter.test.js @@ -4,7 +4,6 @@ const {agentProvider, mockManager, fixtureManager, matchers} = require('../../ut const {anyContentVersion, anyString} = matchers; const crypto = require('crypto'); -const should = require('should'); const Papa = require('papaparse'); const models = require('../../../core/server/models'); const moment = require('moment'); @@ -25,12 +24,12 @@ let tiers, labels, newsletters; function basicAsserts(member, row) { // Basic checks - should(row.email).eql(member.get('email')); - should(row.name).eql(member.get('name')); - should(row.note).eql(member.get('note') || ''); + assert.equal(row.email, member.get('email')); + assert.equal(row.name, member.get('name')); + assert.equal(row.note, member.get('note') || ''); assert.equal(row.deleted_at, ''); - should(row.created_at).eql(moment(member.get('created_at')).toISOString()); + assert.equal(row.created_at, moment(member.get('created_at')).toISOString()); } /** @@ -120,7 +119,7 @@ describe('Members API — exportCSV', function () { basicAsserts(member, row); assert.equal(row.subscribed_to_emails, 'false'); assert.equal(row.complimentary_plan, ''); - should(row.tiers.split(',').sort().join(',')).eql(tiersList); + assert.equal(row.tiers.split(',').sort().join(','), tiersList); }, [`filter=tier:[${tiers[0].get('slug')}]`, 'filter=subscribed:false']); }); @@ -157,7 +156,7 @@ describe('Members API — exportCSV', function () { basicAsserts(member, row); assert.equal(row.subscribed_to_emails, 'false'); assert.equal(row.complimentary_plan, ''); - should(row.labels.split(',').sort().join(',')).eql(labelsList); + assert.equal(row.labels.split(',').sort().join(','), labelsList); assert.equal(row.tiers, ''); }, [`filter=label:${labels[0].get('slug')}`, 'filter=subscribed:false']); }); diff --git a/ghost/core/test/e2e-api/admin/members-importer.test.js b/ghost/core/test/e2e-api/admin/members-importer.test.js index a9107c2fc75..9903e1efa9c 100644 --- a/ghost/core/test/e2e-api/admin/members-importer.test.js +++ b/ghost/core/test/e2e-api/admin/members-importer.test.js @@ -109,7 +109,7 @@ describe('Members Importer API', function () { // .expect('Content-Type', /json/) // .expect('Cache-Control', testUtils.cacheRules.private) // .then((res) => { - // should.not.exist(res.headers['x-cache-invalidate']); + // assert(!res.headers['x-cache-invalidate']); // const jsonResponse = res.body; @@ -130,7 +130,7 @@ describe('Members Importer API', function () { // .expect('Cache-Control', testUtils.cacheRules.private) // .expect(200) // .then((res) => { - // should.not.exist(res.headers['x-cache-invalidate']); + // assert(!res.headers['x-cache-invalidate']); // const jsonResponse = res.body; // assertExists(jsonResponse); // assertExists(jsonResponse.members); @@ -147,7 +147,7 @@ describe('Members Importer API', function () { // .expect('Cache-Control', testUtils.cacheRules.private) // .expect(200) // .then((res) => { - // should.not.exist(res.headers['x-cache-invalidate']); + // assert(!res.headers['x-cache-invalidate']); // const jsonResponse = res.body; // assertExists(jsonResponse); // assertExists(jsonResponse.meta); diff --git a/ghost/core/test/e2e-api/admin/members.test.js b/ghost/core/test/e2e-api/admin/members.test.js index ad3c4db59ae..df64c7c6375 100644 --- a/ghost/core/test/e2e-api/admin/members.test.js +++ b/ghost/core/test/e2e-api/admin/members.test.js @@ -4,7 +4,7 @@ const {queryStringToken} = regexes; const ObjectId = require('bson-objectid').default; const assert = require('node:assert/strict'); -const {assertExists, assertObjectMatches} = require('../../utils/assertions'); +const {assertExists, assertArrayContainsDeep, assertObjectMatches} = require('../../utils/assertions'); const nock = require('nock'); const sinon = require('sinon'); const should = require('should'); @@ -480,7 +480,7 @@ describe('Members API - member attribution', function () { }) .expect(({body}) => { assert.equal(body.events.find(e => e.type !== 'signup_event'), undefined); - should(body.events.map(e => e.data.attribution)).containDeep(signupAttributions); + assertArrayContainsDeep(body.events.map(e => e.data.attribution), signupAttributions); }); }); }); @@ -1860,7 +1860,7 @@ describe('Members API', function () { // Check that the product that we are going to add is not the same as the existing one const product = await getOtherPaidProduct(); assert.equal(memberWithPaidSubscription.tiers.length, 1); - should(memberWithPaidSubscription.tiers[0].id).not.eql(product.id); + assert.notEqual(memberWithPaidSubscription.tiers[0].id, product.id); // Add it manually await models.Member.edit({ diff --git a/ghost/core/test/e2e-api/admin/offers.test.js b/ghost/core/test/e2e-api/admin/offers.test.js index 41316f5a4d3..1cc977b2b4a 100644 --- a/ghost/core/test/e2e-api/admin/offers.test.js +++ b/ghost/core/test/e2e-api/admin/offers.test.js @@ -2,7 +2,6 @@ const assert = require('node:assert/strict'); const {assertObjectMatches} = require('../../utils/assertions'); const {agentProvider, fixtureManager, matchers} = require('../../utils/e2e-framework'); const {anyContentVersion, anyEtag, anyObjectId, anyLocationFor, anyErrorId, anyISODateTime} = matchers; -const should = require('should'); const models = require('../../../core/server/models'); const sinon = require('sinon'); const logging = require('@tryghost/logging'); @@ -297,8 +296,8 @@ describe('Offers API', function () { }] }) .expect(({body}) => { - body.offers[0].redemption_type.should.eql('retention'); - should(body.offers[0].tier).be.null(); + assert.equal(body.offers[0].redemption_type, 'retention'); + assert.equal(body.offers[0].tier, null); }); }); @@ -646,7 +645,7 @@ describe('Offers API', function () { }) }) .expect(({body}) => { - body.offers[0].should.match(updatedOffer); + assertObjectMatches(body.offers[0], updatedOffer); }); }); @@ -809,7 +808,7 @@ describe('Offers API', function () { }) }) .expect(({body}) => { - body.offers[0].tier.id.should.eql(defaultTier.id); + assert.equal(body.offers[0].tier.id, defaultTier.id); }); }); }); diff --git a/ghost/core/test/e2e-api/admin/themes.test.js b/ghost/core/test/e2e-api/admin/themes.test.js index 32f3f8e9037..489da096b3d 100644 --- a/ghost/core/test/e2e-api/admin/themes.test.js +++ b/ghost/core/test/e2e-api/admin/themes.test.js @@ -184,8 +184,6 @@ describe('Themes API', function () { tmpFolderContents.splice(i, 1); } } - tmpFolderContents.should.be.an.Array().with.lengthOf(12); - assert.deepEqual(tmpFolderContents, [ 'broken-theme', 'casper', @@ -234,7 +232,7 @@ describe('Themes API', function () { localUtils.API.checkResponse(jsonResponse.themes[0], 'theme', ['warnings']); assert.equal(jsonResponse.themes[0].name, 'warnings'); assert.equal(jsonResponse.themes[0].active, false); - jsonResponse.themes[0].warnings.should.be.an.Array(); + assert(Array.isArray(jsonResponse.themes[0].warnings)); // Delete the theme to clean up after the test await ownerRequest @@ -285,7 +283,7 @@ describe('Themes API', function () { assertExists(testTheme2); localUtils.API.checkResponse(testTheme2, 'theme', ['warnings', 'templates']); assert.equal(testTheme2.active, true); - testTheme2.warnings.should.be.an.Array(); + assert(Array.isArray(testTheme2.warnings)); // Result should be the same const activeThemeResult = await ownerRequest @@ -323,7 +321,7 @@ describe('Themes API', function () { localUtils.API.checkResponse(jsonResponse.themes[0], 'theme', ['warnings']); assert.equal(jsonResponse.themes[0].name, 'test'); assert.equal(jsonResponse.themes[0].active, false); - jsonResponse.themes[0].warnings.should.be.an.Array(); + assert(Array.isArray(jsonResponse.themes[0].warnings)); // Delete the theme to clean up after the test await ownerRequest @@ -366,7 +364,7 @@ describe('Themes API', function () { localUtils.API.checkResponse(jsonResponse.themes[0], 'theme', ['warnings']); assert.equal(jsonResponse.themes[0].name, 'starter'); assert.equal(jsonResponse.themes[0].active, false); - jsonResponse.themes[0].warnings.should.be.an.Array(); + assert(Array.isArray(jsonResponse.themes[0].warnings)); // Delete the theme to clean up after the test await ownerRequest diff --git a/ghost/core/test/e2e-api/content/tags.test.js b/ghost/core/test/e2e-api/content/tags.test.js index 83e9ca2db44..5fd6f974241 100644 --- a/ghost/core/test/e2e-api/content/tags.test.js +++ b/ghost/core/test/e2e-api/content/tags.test.js @@ -97,8 +97,8 @@ describe('Tags Content API', function () { const jsonResponse = res.body; - assertExists(jsonResponse.tags); - jsonResponse.tags.should.be.an.Array().with.lengthOf(5); + assert(Array.isArray(jsonResponse.tags)); + assert.equal(jsonResponse.tags.length, 5); // Each tag should have the correct count assert.equal(_.find(jsonResponse.tags, {name: 'Getting Started'}).count.posts, 7); diff --git a/ghost/core/test/e2e-api/members-comments/comments.test.js b/ghost/core/test/e2e-api/members-comments/comments.test.js index 5bcf82d7b74..72d0fa372c6 100644 --- a/ghost/core/test/e2e-api/members-comments/comments.test.js +++ b/ghost/core/test/e2e-api/members-comments/comments.test.js @@ -260,10 +260,10 @@ async function testCanCommentOnPost(member) { // Check last_updated_at changed? member = await models.Member.findOne({id: member.id}); - should.notEqual(member.get('last_seen_at'), null, 'The member should have a `last_seen_at` property after posting a comment.'); + assert.notEqual(member.get('last_seen_at'), null, 'The member should have a `last_seen_at` property after posting a comment.'); // Check last_commented_at changed? - should.notEqual(member.get('last_commented_at'), null, 'The member should have a `last_commented_at` property after posting a comment.'); + assert.notEqual(member.get('last_commented_at'), null, 'The member should have a `last_commented_at` property after posting a comment.'); } async function testCanReply(member, emailMatchers = {}) { @@ -294,10 +294,10 @@ async function testCanReply(member, emailMatchers = {}) { // Check last_updated_at changed? member = await models.Member.findOne({id: member.id}); - should.notEqual(member.get('last_seen_at').getTime(), date.getTime(), 'Should update `last_seen_at` property after posting a comment.'); + assert.notEqual(member.get('last_seen_at').getTime(), date.getTime(), 'Should update `last_seen_at` property after posting a comment.'); // Check last_commented_at changed? - should.notEqual(member.get('last_commented_at').getTime(), date.getTime(), 'Should update `last_commented_at` property after posting a comment.'); + assert.notEqual(member.get('last_commented_at').getTime(), date.getTime(), 'Should update `last_commented_at` property after posting a comment.'); } async function testCannotCommentOnPost(status = 403) { @@ -435,7 +435,7 @@ describe('Comments API', function () { // go through all comments and check if the deleted comment is not there data2.body.comments.forEach((comment) => { - should(comment.html).not.eql('This is a deleted comment'); + assert.notEqual(comment.html, 'This is a deleted comment'); }); assert.equal(data2.body.comments.length, 0); @@ -477,8 +477,8 @@ describe('Comments API', function () { // check if hidden and deleted comments have their html removed data2.body.comments.forEach((comment) => { - should.notEqual(comment.html, 'This is a hidden comment'); - should.notEqual(comment.html, 'This is a deleted comment'); + assert.notEqual(comment.html, 'This is a hidden comment'); + assert.notEqual(comment.html, 'This is a deleted comment'); }); // check if hiddenComment.id and deletedComment.id are in the response @@ -845,7 +845,7 @@ describe('Comments API', function () { // get the LAST comment from data2 let lastComment = data2.body.comments[data2.body.comments.length - 1]; - should(lastComment.id).eql(oldestComment.id); + assert.equal(lastComment.id, oldestComment.id); }); it('Can reply to your own comment', async function () { @@ -1107,7 +1107,7 @@ describe('Comments API', function () { assert.equal(body.meta.pagination.next, null); // Check liked + likes working for replies too - should(body.comments[2].id).eql(replies[2].get('id')); + assert.equal(body.comments[2].id, replies[2].get('id')); assert.equal(body.comments[2].count.likes, 1); assert.equal(body.comments[2].liked, true); }); @@ -1170,7 +1170,7 @@ describe('Comments API', function () { assert.equal(reports.models.length, 1); const report = reports.models[0]; - report.get('member_id').should.eql(loggedInMember.id); + assert.equal(report.get('member_id'), loggedInMember.id); mockManager.assert.sentEmail({ subject: '🚩 A comment has been reported on your post', @@ -1196,7 +1196,7 @@ describe('Comments API', function () { assert.equal(reports.models.length, 1); const report = reports.models[0]; - report.get('member_id').should.eql(loggedInMember.id); + assert.equal(report.get('member_id'), loggedInMember.id); emailMockReceiver.assertSentEmailCount(0); }); @@ -1481,7 +1481,7 @@ describe('Comments API', function () { }); // in_reply_to is set - newComment.in_reply_to_id.should.eql(reply.get('id')); + assert.equal(newComment.in_reply_to_id, reply.get('id')); assert.equal(newComment.in_reply_to_snippet, 'This is a reply'); // replied-to comment author is notified diff --git a/ghost/core/test/e2e-api/members/create-stripe-checkout-session.test.js b/ghost/core/test/e2e-api/members/create-stripe-checkout-session.test.js index 60017a639cb..5369842a878 100644 --- a/ghost/core/test/e2e-api/members/create-stripe-checkout-session.test.js +++ b/ghost/core/test/e2e-api/members/create-stripe-checkout-session.test.js @@ -405,9 +405,9 @@ describe('Create Stripe Checkout Session', function () { .reply((uri, body) => { if (uri === '/v1/checkout/sessions') { const parsed = new URLSearchParams(body); - should(parsed.get('metadata[attribution_url]')).eql(url); + assert.equal(parsed.get('metadata[attribution_url]'), url); assert.equal(parsed.get('metadata[attribution_type]'), 'post'); - should(parsed.get('metadata[attribution_id]')).eql(post.id); + assert.equal(parsed.get('metadata[attribution_id]'), post.id); return [200, {id: 'cs_123', url: 'https://site.com'}]; } diff --git a/ghost/core/test/e2e-api/members/webhooks.test.js b/ghost/core/test/e2e-api/members/webhooks.test.js index 00075fcf401..b3013a4655e 100644 --- a/ghost/core/test/e2e-api/members/webhooks.test.js +++ b/ghost/core/test/e2e-api/members/webhooks.test.js @@ -1,8 +1,7 @@ const crypto = require('crypto'); const assert = require('node:assert/strict'); -const {assertObjectMatches} = require('../../utils/assertions'); +const {assertArrayContainsDeep, assertObjectMatches} = require('../../utils/assertions'); const nock = require('nock'); -const should = require('should'); const stripe = require('stripe'); const {Product} = require('../../../core/server/models/product'); const {agentProvider, mockManager, fixtureManager, matchers} = require('../../utils/e2e-framework'); @@ -40,7 +39,9 @@ async function getOfferByStripeCoupon(stripeCouponId) { async function assertMemberEvents({eventType, memberId, asserts}) { const events = (await models[eventType].where('member_id', memberId).fetchAll()).toJSON(); - events.should.match(asserts); + for (let i = 0; i < asserts.length; i++) { + assertObjectMatches(events[i], asserts[i]); + } assert.equal(events.length, asserts.length, `Only ${asserts.length} ${eventType} should have been added.`); } @@ -48,7 +49,8 @@ async function assertSubscription(subscriptionId, asserts) { const subscription = await getSubscription(subscriptionId); // We use the native toJSON to prevent calling the overriden serialize method - models.Base.Model.prototype.serialize.call(subscription).should.match(asserts); + const serialized = models.Base.Model.prototype.serialize.call(subscription); + assertObjectMatches(serialized, asserts); } // Helper methods to update the customer and subscription @@ -327,11 +329,9 @@ describe('Members API', function () { assert.equal(initialMember.status, 'paid', 'The member initial status should be paid'); assert.equal(initialMember.attribution.referrer_medium, 'Ghost Admin', 'The member should have been created via Ghost Admin'); assert.equal(initialMember.tiers.length, 1, 'The member should have one tier'); - should(initialMember.subscriptions).match([ - { - status: 'active' - } - ]); + assertObjectMatches(initialMember.subscriptions[0], { + status: 'active' + }); // Check whether MRR and status has been set await assertSubscription(initialMember.subscriptions[0].id, { @@ -376,13 +376,11 @@ describe('Members API', function () { const {body: body2} = await adminAgent.get('/members/' + initialMember.id + '/'); assert.equal(body2.members.length, 1, 'The member does not exist'); const updatedMember = body2.members[0]; - should(updatedMember.subscriptions).match([ - { - status: 'active', - cancel_at_period_end: true, - cancellation_reason: 'I want to break free' - } - ]); + assertObjectMatches(updatedMember.subscriptions[0], { + status: 'active', + cancel_at_period_end: true, + cancellation_reason: 'I want to break free' + }); // Check whether MRR and cancel_at_period_end has been set await assertSubscription(initialMember.subscriptions[0].id, { @@ -469,11 +467,9 @@ describe('Members API', function () { assert.equal(initialMember.status, 'paid', 'The member initial status should be paid'); assert.equal(initialMember.attribution.referrer_medium, 'Ghost Admin', 'The member should have been created via Ghost Admin'); assert.equal(initialMember.tiers.length, 1, 'The member should have one tier'); - should(initialMember.subscriptions).match([ - { - status: 'active' - } - ]); + assertObjectMatches(initialMember.subscriptions[0], { + status: 'active' + }); // Check whether MRR and status has been set await assertSubscription(initialMember.subscriptions[0].id, { @@ -520,12 +516,10 @@ describe('Members API', function () { const updatedMember = body2.members[0]; assert.equal(updatedMember.status, 'free'); assert.equal(updatedMember.tiers.length, 0, 'The member should have no products'); - should(updatedMember.subscriptions).match([ - { - status: 'canceled', - cancellation_reason: 'Payment failed' - } - ]); + assertObjectMatches(updatedMember.subscriptions[0], { + status: 'canceled', + cancellation_reason: 'Payment failed' + }); // Check whether MRR and status has been set await assertSubscription(initialMember.subscriptions[0].id, { @@ -697,11 +691,9 @@ describe('Members API', function () { const initialMember = await createMemberFromStripe(); assert.equal(initialMember.status, 'comped', 'The member initial status should be comped'); assert.equal(initialMember.tiers.length, 1, 'The member should have one tier'); - should(initialMember.subscriptions).match([ - { - status: 'active' - } - ]); + assertObjectMatches(initialMember.subscriptions[0], { + status: 'active' + }); // Cancel the previously created subscription in Stripe set(subscription, { @@ -733,11 +725,9 @@ describe('Members API', function () { const updatedMember = body2.members[0]; assert.equal(updatedMember.status, 'free'); assert.equal(updatedMember.tiers.length, 0, 'The member should have no products'); - should(updatedMember.subscriptions).match([ - { - status: 'canceled' - } - ]); + assertObjectMatches(updatedMember.subscriptions[0], { + status: 'canceled' + }); // Check the status events for this newly created member (should be NULL -> paid only) await assertMemberEvents({ @@ -1938,11 +1928,7 @@ describe('Members API', function () { }); // Check whether the offer attribute is passed correctly in the response when fetching a single member - member.subscriptions[0].should.match({ - offer: { - id: offer_id - } - }); + assert.equal(member.subscriptions[0].offer?.id, offer_id); await assertMemberEvents({ eventType: 'MemberPaidSubscriptionEvent', @@ -1985,14 +1971,12 @@ describe('Members API', function () { const updatedMember = body2.members[0]; assert.equal(updatedMember.status, 'free'); assert.equal(updatedMember.tiers.length, 0, 'The member should have no products'); - should(updatedMember.subscriptions).match([ - { - status: 'canceled', - offer: { - id: offer_id - } + assertObjectMatches(updatedMember.subscriptions[0], { + status: 'canceled', + offer: { + id: offer_id } - ]); + }); // Check whether MRR and status has been set await assertSubscription(member.subscriptions[0].id, { @@ -2392,11 +2376,7 @@ describe('Members API', function () { }); // Check whether the offer attribute is passed correctly in the response when fetching a single member - updatedMember.subscriptions[0].should.match({ - offer: { - id: offer.id - } - }); + assert.equal(updatedMember.subscriptions[0].offer?.id, offer.id); await assertMemberEvents({ eventType: 'MemberPaidSubscriptionEvent', @@ -2546,11 +2526,7 @@ describe('Members API', function () { }); // Check whether the offer attribute is passed correctly in the response when fetching a single member - member.subscriptions[0].should.match({ - offer: { - id: createdOffer.id - } - }); + assert.equal(member.subscriptions[0].offer?.id, createdOffer.id); await assertMemberEvents({ eventType: 'MemberPaidSubscriptionEvent', @@ -2768,8 +2744,8 @@ describe('Members API', function () { etag: anyEtag }) .expect(({body: body3}) => { - should(body3.members[0].attribution).eql(attributionResource); - should(body3.members[0].subscriptions[0].attribution).eql(attributionResource); + assert.deepEqual(body3.members[0].attribution, attributionResource); + assert.deepEqual(body3.members[0].subscriptions[0].attribution, attributionResource); subscriptionAttributions.push(body3.members[0].subscriptions[0].attribution); }); @@ -3218,7 +3194,7 @@ describe('Members API', function () { }) .expect(({body}) => { assert.equal(body.events.find(e => e.type !== 'subscription_event'), undefined); - should(body.events.map(e => e.data.attribution)).containDeep(subscriptionAttributions); + assertArrayContainsDeep(body.events.map(e => e.data.attribution), subscriptionAttributions); }); }); }); diff --git a/ghost/core/test/e2e-browser/admin/announcement-bar-settings.spec.js b/ghost/core/test/e2e-browser/admin/announcement-bar-settings.spec.js index b2c3c115779..db830cb5c00 100644 --- a/ghost/core/test/e2e-browser/admin/announcement-bar-settings.spec.js +++ b/ghost/core/test/e2e-browser/admin/announcement-bar-settings.spec.js @@ -49,7 +49,7 @@ test.describe('Announcement Bar Settings', () => { async function goToAnnouncementBarSettings(sharedPage) { await test.step('Navigate to the announcement bar settings', async () => { - await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await sharedPage.getByTestId('announcement-bar').getByRole('button', {name: 'Customize'}).click(); // Wait for the preview to load await getPreviewFrame(sharedPage).locator('body *:visible').first().waitFor(); diff --git a/ghost/core/test/e2e-browser/admin/membership-settings.spec.js b/ghost/core/test/e2e-browser/admin/membership-settings.spec.js index 8ba487e2593..1ca01db6a76 100644 --- a/ghost/core/test/e2e-browser/admin/membership-settings.spec.js +++ b/ghost/core/test/e2e-browser/admin/membership-settings.spec.js @@ -11,7 +11,7 @@ test.describe('Membership Settings', () => { // Open Portal settings await sharedPage.goto('/ghost'); - await sharedPage.locator('.gh-nav a[href="#/settings/"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await sharedPage.getByTestId('portal').getByRole('button', {name: 'Customize'}).click(); const modal = sharedPage.getByTestId('portal-modal'); diff --git a/ghost/core/test/e2e-browser/admin/portal-settings.spec.js b/ghost/core/test/e2e-browser/admin/portal-settings.spec.js index 461af941996..64a0c9bda71 100644 --- a/ghost/core/test/e2e-browser/admin/portal-settings.spec.js +++ b/ghost/core/test/e2e-browser/admin/portal-settings.spec.js @@ -5,7 +5,7 @@ test.describe('Portal Settings', () => { test.describe('Links', () => { const openPortalLinks = async (sharedPage) => { await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await sharedPage.getByTestId('portal').getByRole('button', {name: 'Customize'}).click(); diff --git a/ghost/core/test/e2e-browser/admin/private-site.spec.js b/ghost/core/test/e2e-browser/admin/private-site.spec.js index 1aeb39f87cf..87b2b27064e 100644 --- a/ghost/core/test/e2e-browser/admin/private-site.spec.js +++ b/ghost/core/test/e2e-browser/admin/private-site.spec.js @@ -7,7 +7,7 @@ test.describe('Site Settings', () => { // set private mode in admin "on" await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); const section = sharedPage.getByTestId('locksite'); diff --git a/ghost/core/test/e2e-browser/admin/publishing.spec.js b/ghost/core/test/e2e-browser/admin/publishing.spec.js index 566b22fd97b..da781d0917e 100644 --- a/ghost/core/test/e2e-browser/admin/publishing.spec.js +++ b/ghost/core/test/e2e-browser/admin/publishing.spec.js @@ -68,7 +68,7 @@ const checkPostPublished = async (page, {slug, title, body}) => { * @param {String} [options.body] */ const createPage = async (page, {title = 'Hello world', body = 'This is my post body.'} = {}) => { - await page.locator('.gh-nav a[href="#/pages/"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Pages'}).click(); // Create a new post await page.locator('[data-test-new-page-button]').click(); @@ -620,7 +620,7 @@ test.describe('Updating post access', () => { await closePublishFlow(page); // go to settings and change the timezone - await page.locator('[data-test-nav="settings"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await expect(page.getByTestId('timezone')).toContainText('UTC'); await page.getByTestId('timezone-select').click(); @@ -630,7 +630,7 @@ test.describe('Updating post access', () => { await expect(page.getByTestId('timezone')).toContainText('(GMT +9:00) Osaka, Sapporo, Tokyo'); await page.getByTestId('exit-settings').click(); - await page.locator('[data-test-nav="posts"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Posts'}).click(); await page.locator('[data-test-post-id]', {hasText: /Published in timezones/}).click(); await openPostSettingsMenu(page); diff --git a/ghost/core/test/e2e-browser/admin/site-settings.spec.js b/ghost/core/test/e2e-browser/admin/site-settings.spec.js index 3f5e54a0efb..a882f41767e 100644 --- a/ghost/core/test/e2e-browser/admin/site-settings.spec.js +++ b/ghost/core/test/e2e-browser/admin/site-settings.spec.js @@ -3,7 +3,7 @@ const test = require('../fixtures/ghost-test'); const {createPostDraft, createTier, disconnectStripe, generateStripeIntegrationToken, setupStripe, getStripeAccountId} = require('../utils'); const changeSubscriptionAccess = async (page, access) => { - await page.locator('[data-test-nav="settings"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); const section = page.getByTestId('access'); const select = section.getByTestId('subscription-access-select'); diff --git a/ghost/core/test/e2e-browser/portal/invites.spec.js b/ghost/core/test/e2e-browser/portal/invites.spec.js index 408bb093fd3..5e76085acde 100644 --- a/ghost/core/test/e2e-browser/portal/invites.spec.js +++ b/ghost/core/test/e2e-browser/portal/invites.spec.js @@ -10,8 +10,7 @@ test.describe('Portal', () => { test('New staff member can signup using an invite link', async ({sharedPage}) => { // Navigate to settings await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); - await sharedPage.waitForLoadState('networkidle'); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); const testEmail = `test-${Date.now()}@gmail.com`; @@ -51,7 +50,6 @@ test.describe('Portal', () => { await sharedPage.goto(inviteUrl); // Verify we're on the signup page - await sharedPage.waitForLoadState('networkidle'); await expect(sharedPage.locator('text=Create your account.')).toBeVisible(); //Signup using the invite Link @@ -59,10 +57,10 @@ test.describe('Portal', () => { await sharedPage.getByPlaceholder('jamie@example.com').fill(testEmail); await sharedPage.getByPlaceholder('At least 10 characters').fill('test123456'); await sharedPage.getByRole('button', {name: 'Create Account →'}).click(); - await sharedPage.waitForLoadState('networkidle'); - await expect(sharedPage).toHaveURL(/\/ghost\/#\/.*/); + await expect(sharedPage).toHaveURL(/\/ghost\/#\/.*/, {timeout: 30000}); - await sharedPage.locator('[data-test-nav="arrow-down"]').click(); + // Invited users are Contributors, which get a floating user menu instead of the sidebar + await sharedPage.getByRole('button', {name: 'Open user menu'}).click(); await expect(sharedPage.locator(`text=${testEmail}`)).toBeVisible(); await signOutCurrentUser(sharedPage); @@ -74,8 +72,7 @@ test.describe('Portal', () => { test('New staff member can signup using an invite link with 2FA enabled', async ({sharedPage}) => { // Navigate to settings await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); - await sharedPage.waitForLoadState('networkidle'); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); const testEmail = `test-${Date.now()}@gmail.com`; @@ -108,8 +105,6 @@ test.describe('Portal', () => { const encodedToken = security.url.encodeBase64(token); const adminUrl = new URL(sharedPage.url()).origin + '/ghost'; const inviteUrl = `${adminUrl}/signup/${encodedToken}/`; - const context = await sharedPage.context(); - await context.clearCookies(); await signOutCurrentUser(sharedPage); @@ -117,7 +112,6 @@ test.describe('Portal', () => { await sharedPage.goto(inviteUrl); // Verify we're on the signup page - await sharedPage.waitForLoadState('networkidle'); await expect(sharedPage.locator('text=Create your account.')).toBeVisible(); //Signup using the invite Link @@ -125,11 +119,10 @@ test.describe('Portal', () => { await sharedPage.getByPlaceholder('jamie@example.com').fill(testEmail); await sharedPage.getByPlaceholder('At least 10 characters').fill('test123456'); await sharedPage.getByRole('button', {name: 'Create Account →'}).click(); - await sharedPage.waitForLoadState('networkidle'); + await expect(sharedPage).toHaveURL(/\/ghost\/#\/.*/, {timeout: 30000}); - await expect(sharedPage).toHaveURL(/\/ghost\/#\/.*/); - - await sharedPage.locator('[data-test-nav="arrow-down"]').click(); + // Invited users are Contributors, which get a floating user menu instead of the sidebar + await sharedPage.getByRole('button', {name: 'Open user menu'}).click(); await expect(sharedPage.locator(`text=${testEmail}`)).toBeVisible(); await signOutCurrentUser(sharedPage); diff --git a/ghost/core/test/e2e-browser/portal/member-actions.spec.js b/ghost/core/test/e2e-browser/portal/member-actions.spec.js index 217d48916e0..3ea1faca1ef 100644 --- a/ghost/core/test/e2e-browser/portal/member-actions.spec.js +++ b/ghost/core/test/e2e-browser/portal/member-actions.spec.js @@ -7,7 +7,7 @@ const {createMember, impersonateMember} = require('../utils'); */ const addNewsletter = async (page) => { await page.goto('/ghost'); - await page.locator('[data-test-nav="settings"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); // create newsletter const section = page.getByTestId('newsletters'); diff --git a/ghost/core/test/e2e-browser/portal/offers.spec.js b/ghost/core/test/e2e-browser/portal/offers.spec.js index 658955b55ce..5f4b60b205a 100644 --- a/ghost/core/test/e2e-browser/portal/offers.spec.js +++ b/ghost/core/test/e2e-browser/portal/offers.spec.js @@ -30,7 +30,7 @@ test.describe('Portal', () => { // check that offer was added in the offer list screen await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await expect(await sharedPage.getByTestId('offers')).toContainText(offerName); await sharedPage.goto(offerLink); @@ -71,7 +71,7 @@ test.describe('Portal', () => { // go to member list on admin await sharedPage.goto('/ghost'); - await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Members'}).click(); // // 1 member, should be Testy, on Portal Tier await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+trial@example.com'}), 'Should have 1 paid member').toBeVisible(); @@ -107,7 +107,7 @@ test.describe('Portal', () => { // check that offer was added in the offer list screen await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await expect(sharedPage.getByTestId('offers')).toContainText(offerName); // open offer details page // await sharedPage.locator(`[data-test-offer="${offerName}"] a`).first().click(); @@ -153,7 +153,7 @@ test.describe('Portal', () => { // go to members list on admin await sharedPage.goto('/ghost'); - await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Members'}).click(); // 1 member, should be Testy, on Portal Tier await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+oneoff@example.com'}), 'Should have 1 paid member').toBeVisible(); @@ -184,7 +184,7 @@ test.describe('Portal', () => { }); await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await expect(await sharedPage.getByTestId('offers')).toContainText(offerName); await sharedPage.goto(offerLink); @@ -228,7 +228,7 @@ test.describe('Portal', () => { // Discounted price should not be visible for member for one-time offers await expect(portalFrameLocator.locator('text=$5.40/month'), 'Portal should show discounted price').toBeVisible(); await sharedPage.goto('/ghost'); - await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Members'}).click(); // 1 member, should be Testy, on Portal Tier await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+multi@example.com'}), 'Should have 1 paid member').toBeVisible(); @@ -259,7 +259,7 @@ test.describe('Portal', () => { // check that offer was added in the offer list screen await sharedPage.goto('/ghost'); - await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await expect(sharedPage.getByTestId('offers')).toContainText(offerName); await sharedPage.goto(offerLink); @@ -301,7 +301,7 @@ test.describe('Portal', () => { // Discounted price should be visible for member for forever offers await expect(portalFrameLocator.locator('text=$5.40/month'), 'Portal should show discounted price').toBeVisible(); await sharedPage.goto('/ghost'); - await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); + await sharedPage.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Members'}).click(); // 1 member, should be Testy, on Portal Tier await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+forever@example.com'}), 'Should have 1 paid member').toBeVisible(); diff --git a/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js b/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js index 6f4eaa5f2fa..ad929ab477a 100644 --- a/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js +++ b/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js @@ -36,7 +36,7 @@ const setupGhost = async (page) => { const action = await Promise.race([ page.locator('.gh-signin').waitFor(options).then(() => actions.signin).catch(() => {}), page.locator('.gh-setup').waitFor(options).then(() => actions.setup).catch(() => {}), - page.locator('.gh-nav').waitFor(options).then(() => actions.noAction).catch(() => {}) + page.locator('[data-sidebar="sidebar"]').waitFor(options).then(() => actions.noAction).catch(() => {}) ]); // Add owner user data from usual fixture @@ -56,7 +56,7 @@ const setupGhost = async (page) => { await page.getByPlaceholder('At least 10 characters').press('Enter'); - await page.locator('.gh-nav').waitFor(options); + await page.locator('[data-sidebar="sidebar"]').waitFor(options); } }; @@ -70,18 +70,17 @@ const signInAsUserById = async (page, userId) => { await page.locator('#password').fill(user.password); await page.getByRole('button', {name: 'Sign in'}).click(); // Confirm we have reached Ghost Admin - await page.locator('.gh-nav').waitFor({state: 'visible', timeout: 10000}); + await page.locator('[data-sidebar="sidebar"]').waitFor({state: 'visible', timeout: 10000}); }; const signOutCurrentUser = async (page) => { await page.goto('/ghost/#/signout'); - await page.waitForLoadState('networkidle'); await page.locator('.gh-signin').waitFor({state: 'visible', timeout: 10000}); }; const disconnectStripe = async (page) => { await deleteAllMembers(page); - await page.locator('.gh-nav a[href="#/settings/"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await page.getByTestId('tiers').waitFor(); if (await page.isVisible('[data-testid="stripe-connected"]')) { await page.getByTestId('stripe-connected').first().click(); @@ -92,7 +91,7 @@ const disconnectStripe = async (page) => { const setupStripe = async (page, stripConnectIntegrationToken) => { await deleteAllMembers(page); - await page.locator('.gh-nav a[href="#/settings/"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); await page.getByTestId('tiers').waitFor(); if (await page.isVisible('[data-testid="stripe-connected"]')) { // Disconnect if already connected @@ -115,7 +114,7 @@ const setupStripe = async (page, stripConnectIntegrationToken) => { // Setup Mailgun with fake data for Ghost Admin to allow bulk sending const setupMailgun = async (page) => { - await page.locator('.gh-nav a[href="#/settings/"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); const section = page.getByTestId('mailgun'); await section.getByRole('button', {name: 'Edit'}).click(); @@ -181,10 +180,10 @@ const impersonateMember = async (page) => { const createTier = async (page, {name, monthlyPrice, yearlyPrice, trialDays}, enableInPortal = true) => { await test.step('Create a tier', async () => { // Navigate to the member settings - await page.locator('[data-test-nav="settings"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); - // Tiers request can take time, so waiting until there is no connections before interacting with them - await page.waitForLoadState('networkidle'); + // Tiers request can take time, so waiting until the Add tier button is visible before interacting + await page.getByTestId('tiers').getByRole('button', {name: 'Add tier'}).waitFor(); // Archive if already exists while (await page.getByTestId('tier-card').filter({hasText: name}).first().isVisible()) { @@ -253,13 +252,11 @@ const createOffer = async (page, {name, tierName, offerType, amount, discountTyp let offerLink; await test.step('Create an offer', async () => { await page.goto('/ghost'); - await page.locator('[data-test-nav="settings"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); // Keep offer names unique & <= 40 characters offerName = `${name} (${new ObjectID().toHexString().slice(0, 40 - name.length - 3)})`; - // Tiers request can take time, so waiting until there is no connections before interacting with them - await page.waitForLoadState('networkidle'); - // ... and even so, the component updates can take a bit to trickle down, so we should verify that the Tier is fully loaded before proceeding + // Verify that the Tier is fully loaded before proceeding await page.getByTestId('tiers').getByText('No active tiers found').waitFor({state: 'hidden'}); await page.getByTestId('offers').getByRole('button', {name: 'Manage tiers'}).waitFor({state: 'hidden'}); @@ -314,7 +311,6 @@ const createOffer = async (page, {name, tierName, offerType, amount, discountTyp await chooseOptionInSelect(page.getByTestId('tier-cadence-select-offers'), `${tierName} - Monthly`); await page.getByRole('button', {name: 'Publish'}).click(); - await page.waitForLoadState('networkidle'); const offerLinkInput = await page.locator('input[name="offer-url"]'); // sometimes offer link is not generated, and if so the rest of the test will fail @@ -372,7 +368,7 @@ const completeStripeSubscription = async (page, {awaitNetworkIdle = true} = {}) */ const createMember = async (page, {email, name, note, label = '', compedPlan}) => { await page.goto('/ghost'); - await page.locator('.gh-nav a[href="#/members/"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Members'}).click(); await page.waitForSelector('a[href="#/members/new/"] span'); await page.locator('a[href="#/members/new/"] span:has-text("New member")').click(); await page.waitForSelector('input[name="name"]'); @@ -412,7 +408,7 @@ const createMember = async (page, {email, name, note, label = '', compedPlan}) = * @param {String} [options.body] */ const createPostDraft = async (page, {title = 'Hello world', body = 'This is my post body.'} = {}) => { - await page.locator('.gh-nav a[href="#/posts/"]').click(); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Posts'}).click(); // Create a new post await page.locator('[data-test-new-post-button]').click(); @@ -439,9 +435,9 @@ const createPostDraft = async (page, {title = 'Hello world', body = 'This is my const goToMembershipPage = async (page) => { return await test.step('Open Membership settings', async () => { await page.goto('/ghost'); - await page.locator('[data-test-nav="settings"]').click(); - // Tiers request can take time, so waiting until there is no connections before interacting with UI - await page.waitForLoadState('networkidle'); + await page.locator('[data-sidebar="sidebar"]').getByRole('link', {name: 'Settings'}).click(); + // Tiers request can take time, so waiting until the tiers section is loaded before interacting with UI + await page.getByTestId('tiers').waitFor(); }); }; diff --git a/ghost/core/test/e2e-frontend/helpers/get.test.js b/ghost/core/test/e2e-frontend/helpers/get.test.js index 57fdf36188f..d85d65e44d6 100644 --- a/ghost/core/test/e2e-frontend/helpers/get.test.js +++ b/ghost/core/test/e2e-frontend/helpers/get.test.js @@ -30,7 +30,7 @@ function buildMember(status, products = []) { } function testPosts(posts, map) { - posts.should.be.an.Array(); + assert(Array.isArray(posts)); posts.length.should.eql(DEFAULT_POST_FIXTURE_COUNT + Object.keys(map).length); // Free post diff --git a/ghost/core/test/integration/migrations/nullable-utils.test.js b/ghost/core/test/integration/migrations/nullable-utils.test.js index be3bc8b890c..185050cc006 100644 --- a/ghost/core/test/integration/migrations/nullable-utils.test.js +++ b/ghost/core/test/integration/migrations/nullable-utils.test.js @@ -159,7 +159,7 @@ describe('Migrations - schema utils', function () { await migration.up({transacting}); await transacting.commit(); - should.ok(logSpy.calledWith(sinon.match('skipping as column is already nullable')), 'Should log skip message'); + assert(logSpy.calledWith(sinon.match('skipping as column is already nullable')), 'Should log skip message'); // Column should still be nullable const isNullableAfter = await isColumnNullable(tableName, 'nullable_col'); @@ -235,7 +235,7 @@ describe('Migrations - schema utils', function () { await migration.up({transacting}); await transacting.commit(); - should.ok(logSpy.calledWith(sinon.match('skipping as column is already not nullable')), 'Should log skip message'); + assert(logSpy.calledWith(sinon.match('skipping as column is already not nullable')), 'Should log skip message'); // Column should still be not nullable const isNotNullableAfter = await isColumnNotNullable(tableName, 'not_nullable_col'); @@ -321,12 +321,11 @@ describe('Migrations - schema utils', function () { if (dbUtils.isMySQL()) { // MySQL should log a warning when checking nullable status fails - should.ok(logWarnSpy.calledWith(sinon.match('Could not check nullable status')), - 'MySQL should log warning about checking nullable status'); + assert(logWarnSpy.calledWith(sinon.match('Could not check nullable status')), 'MySQL should log warning about checking nullable status'); } // Both databases should eventually fail when trying to ALTER the non-existent table - should.ok(errorThrown, 'Should throw an error when trying to alter non-existent table'); + assert(errorThrown, 'Should throw an error when trying to alter non-existent table'); // The error message varies between databases and Knex versions // SQLite might give a Knex internal error or a 'no such table' error @@ -335,7 +334,7 @@ describe('Migrations - schema utils', function () { errorMessage.includes('Cannot read properties of undefined') || errorMessage.includes('SQLITE_ERROR'); - should.ok(isExpectedError, `Error should be related to missing table, but was: ${errorMessage}`); + assert(isExpectedError, `Error should be related to missing table, but was: ${errorMessage}`); }); }); }); diff --git a/ghost/core/test/legacy/api/admin/posts.test.js b/ghost/core/test/legacy/api/admin/posts.test.js index f9d6e700517..da2450f02a6 100644 --- a/ghost/core/test/legacy/api/admin/posts.test.js +++ b/ghost/core/test/legacy/api/admin/posts.test.js @@ -112,7 +112,7 @@ describe('Posts API', function () { localUtils.API.checkResponse(jsonResponse, 'posts'); assert.equal(jsonResponse.posts.length, 2); jsonResponse.posts.forEach((post) => { - should.notEqual(post.meta_description, null); + assert.notEqual(post.meta_description, null); }); localUtils.API.checkResponse( diff --git a/ghost/core/test/legacy/api/admin/update-user-last-seen.test.js b/ghost/core/test/legacy/api/admin/update-user-last-seen.test.js index 145bb642757..4ce9d74c081 100644 --- a/ghost/core/test/legacy/api/admin/update-user-last-seen.test.js +++ b/ghost/core/test/legacy/api/admin/update-user-last-seen.test.js @@ -1,6 +1,6 @@ +const assert = require('assert/strict'); const {assertExists} = require('../../../utils/assertions'); const sinon = require('sinon'); -const should = require('should'); const {DataGenerator} = require('../../../utils'); const {agentProvider, fixtureManager} = require('../../../utils/e2e-framework'); @@ -58,7 +58,7 @@ describe('Update User Last Seen', function () { const ownerAfter = await models.User.findOne({id: userId}); assertExists(ownerAfter); - should(ownerAfter.get('last_seen')).not.eql(lastSeen); + assert.notDeepEqual(ownerAfter.get('last_seen'), lastSeen); }); it('Should only update last seen after 1 hour', async function () { @@ -75,7 +75,7 @@ describe('Update User Last Seen', function () { const ownerAfter = await models.User.findOne({id: userId}); assertExists(ownerAfter); - should(ownerAfter.get('last_seen')).eql(lastSeen); + assert.deepEqual(ownerAfter.get('last_seen'), lastSeen); }); it('Should always update last seen after login', async function () { @@ -87,7 +87,7 @@ describe('Update User Last Seen', function () { const ownerAfter = await models.User.findOne({id: userId}); assertExists(ownerAfter); - should(ownerAfter.get('last_seen')).not.eql(lastSeen); + assert.notDeepEqual(ownerAfter.get('last_seen'), lastSeen); }); it('Should not update last seen for suspended users', async function () { @@ -112,6 +112,6 @@ describe('Update User Last Seen', function () { const ownerAfter = await models.User.findOne({id: userId}); assertExists(ownerAfter); - should(ownerAfter.get('last_seen')).eql(lastSeen); + assert.deepEqual(ownerAfter.get('last_seen'), lastSeen); }); }); diff --git a/ghost/core/test/legacy/api/content/authors.test.js b/ghost/core/test/legacy/api/content/authors.test.js index 186c198e3c7..c36fc85ae62 100644 --- a/ghost/core/test/legacy/api/content/authors.test.js +++ b/ghost/core/test/legacy/api/content/authors.test.js @@ -120,7 +120,8 @@ describe('Authors Content API', function () { .then((res) => { const jsonResponse = res.body; - jsonResponse.authors.should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(jsonResponse.authors)); + assert.equal(jsonResponse.authors.length, 3); assert.equal(jsonResponse.authors[0].slug, 'joe-bloggs'); assert.equal(jsonResponse.authors[1].slug, 'ghost'); assert.equal(jsonResponse.authors[2].slug, 'slimer-mcectoplasm'); diff --git a/ghost/core/test/legacy/api/content/pages.test.js b/ghost/core/test/legacy/api/content/pages.test.js index b3263f9a953..2323e00b5ad 100644 --- a/ghost/core/test/legacy/api/content/pages.test.js +++ b/ghost/core/test/legacy/api/content/pages.test.js @@ -114,7 +114,8 @@ describe('api/endpoints/content/pages', function () { .then((res) => { const jsonResponse = res.body; - jsonResponse.pages.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(jsonResponse.pages)); + assert.equal(jsonResponse.pages.length, 1); assert.equal(jsonResponse.pages[0].slug, 'static-page-test'); }); }); diff --git a/ghost/core/test/legacy/api/content/posts.test.js b/ghost/core/test/legacy/api/content/posts.test.js index 35351a8f75a..9cb183426dd 100644 --- a/ghost/core/test/legacy/api/content/posts.test.js +++ b/ghost/core/test/legacy/api/content/posts.test.js @@ -208,7 +208,8 @@ describe('api/endpoints/content/posts', function () { } const jsonResponse = res.body; - jsonResponse.posts.should.be.an.Array().with.lengthOf(13); + assert(Array.isArray(jsonResponse.posts)); + assert.equal(jsonResponse.posts.length, 13); done(); }); @@ -222,7 +223,8 @@ describe('api/endpoints/content/posts', function () { .then((res) => { const jsonResponse = res.body; - jsonResponse.posts.should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(jsonResponse.posts)); + assert.equal(jsonResponse.posts.length, 3); assert.equal(jsonResponse.posts[0].slug, 'write'); assert.equal(jsonResponse.posts[1].slug, 'ghostly-kitchen-sink'); assert.equal(jsonResponse.posts[2].slug, 'grow'); @@ -237,7 +239,8 @@ describe('api/endpoints/content/posts', function () { .then((res) => { const jsonResponse = res.body; - jsonResponse.posts.should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(jsonResponse.posts)); + assert.equal(jsonResponse.posts.length, 3); assert.equal(jsonResponse.posts[0].slug, 'write'); assert.equal(jsonResponse.posts[1].slug, 'grow'); assert.equal(jsonResponse.posts[2].slug, 'ghostly-kitchen-sink'); diff --git a/ghost/core/test/legacy/api/content/tags.test.js b/ghost/core/test/legacy/api/content/tags.test.js index 2047c380921..3ded53801c0 100644 --- a/ghost/core/test/legacy/api/content/tags.test.js +++ b/ghost/core/test/legacy/api/content/tags.test.js @@ -75,7 +75,8 @@ describe('api/endpoints/content/tags', function () { .then((res) => { const jsonResponse = res.body; - jsonResponse.tags.should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(jsonResponse.tags)); + assert.equal(jsonResponse.tags.length, 3); assert.equal(jsonResponse.tags[0].slug, 'kitchen-sink'); assert.equal(jsonResponse.tags[1].slug, 'bacon'); assert.equal(jsonResponse.tags[2].slug, 'chorizo'); diff --git a/ghost/core/test/legacy/models/model-member-stripe-customer.test.js b/ghost/core/test/legacy/models/model-member-stripe-customer.test.js index a1e5c4337f8..3c20c8be87d 100644 --- a/ghost/core/test/legacy/models/model-member-stripe-customer.test.js +++ b/ghost/core/test/legacy/models/model-member-stripe-customer.test.js @@ -179,12 +179,12 @@ describe('MemberStripeCustomer Model', function run() { const customerAfterDestroy = await MemberStripeCustomer.findOne({ customer_id: 'fake_customer_id' }); - should.not.exist(customerAfterDestroy, 'MemberStripeCustomer should have been destroyed'); + assert.equal(customerAfterDestroy, null, 'MemberStripeCustomer should have been destroyed'); const subscriptionAfterDestroy = await StripeCustomerSubscription.findOne({ customer_id: 'fake_customer_id' }); - should.not.exist(subscriptionAfterDestroy, 'StripeCustomerSubscription should have been destroyed'); + assert.equal(subscriptionAfterDestroy, null, 'StripeCustomerSubscription should have been destroyed'); }); }); }); diff --git a/ghost/core/test/legacy/models/model-members.test.js b/ghost/core/test/legacy/models/model-members.test.js index d46177cf606..1b5560c6a84 100644 --- a/ghost/core/test/legacy/models/model-members.test.js +++ b/ghost/core/test/legacy/models/model-members.test.js @@ -246,23 +246,23 @@ describe('Member Model', function run() { const memberAfterDestroy = await Member.findOne({ email: 'test@test.member' }); - should.not.exist(memberAfterDestroy, 'Member should have been destroyed'); + assert.equal(memberAfterDestroy, null, 'Member should have been destroyed'); - const memberLabelAfterDestroy = await BaseModel.knex('members_labels').where({ + const memberLabelsAfterDestroy = await BaseModel.knex('members_labels').where({ label_id: label.get('id'), member_id: member.get('id') - }).select().first(); - should.not.exist(memberLabelAfterDestroy, 'Label should have been removed from member'); + }).select(); + assert.deepEqual(memberLabelsAfterDestroy, [], 'Label should have been removed from member'); const customerAfterDestroy = await MemberStripeCustomer.findOne({ member_id: member.get('id') }); - should.not.exist(customerAfterDestroy, 'MemberStripeCustomer should have been destroyed'); + assert.equal(customerAfterDestroy, null, 'MemberStripeCustomer should have been destroyed'); const subscriptionAfterDestroy = await StripeCustomerSubscription.findOne({ customer_id: customer.get('customer_id') }); - should.not.exist(subscriptionAfterDestroy, 'StripeCustomerSubscription should have been destroyed'); + assert.equal(subscriptionAfterDestroy, null, 'StripeCustomerSubscription should have been destroyed'); }); }); @@ -354,7 +354,7 @@ describe('Member Model', function run() { const podcastProductMembers = await Member.findPage({filter: 'products:podcast'}); const foundMemberInPodcast = podcastProductMembers.data.find(model => model.id === member.id); - should.not.exist(foundMemberInPodcast, 'Member should not have been included in products filter'); + assert.equal(foundMemberInPodcast, undefined, 'Member should not have been included in products filter'); }); }); diff --git a/ghost/core/test/legacy/models/model-posts.test.js b/ghost/core/test/legacy/models/model-posts.test.js index e309d05920c..01c06f96d1b 100644 --- a/ghost/core/test/legacy/models/model-posts.test.js +++ b/ghost/core/test/legacy/models/model-posts.test.js @@ -2011,7 +2011,8 @@ describe('Post Model', function () { it('should create the test data correctly', function (done) { // creates a test tag assertExists(tagJSON); - tagJSON.should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(tagJSON)); + assert.equal(tagJSON.length, 3); assert.equal(tagJSON[0].name, 'existing tag a'); assert.equal(tagJSON[1].name, 'existing-tag-b'); @@ -2020,8 +2021,8 @@ describe('Post Model', function () { // creates a test post with an array of tags in the correct order assertExists(postJSON); assert.equal(postJSON.title, 'HTML Ipsum'); - assertExists(postJSON.tags); - postJSON.tags.should.be.an.Array().and.have.lengthOf(3); + assert(Array.isArray(postJSON.tags)); + assert.equal(postJSON.tags.length, 3); assert.equal(postJSON.tags[0].name, 'tag1'); assert.equal(postJSON.tags[1].name, 'tag2'); diff --git a/ghost/core/test/unit/frontend/helpers/get.test.js b/ghost/core/test/unit/frontend/helpers/get.test.js index 97d7be00a86..c2aaf3afada 100644 --- a/ghost/core/test/unit/frontend/helpers/get.test.js +++ b/ghost/core/test/unit/frontend/helpers/get.test.js @@ -239,7 +239,8 @@ describe('{{#get}} helper', function () { 'posts', {hash: {filter: 'tags:[{{post.tags}}]'}, data: locals, fn: fn, inverse: inverse} ).then(function () { - browseStub.firstCall.args.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(browseStub.firstCall.args)); + assert.equal(browseStub.firstCall.args.length, 1); browseStub.firstCall.args[0].should.be.an.Object().with.property('filter'); assert.equal(browseStub.firstCall.args[0].filter, 'tags:[test,magic]'); @@ -253,7 +254,8 @@ describe('{{#get}} helper', function () { 'posts', {hash: {filter: 'author:{{post.author}}'}, data: locals, fn: fn, inverse: inverse} ).then(function () { - browseStub.firstCall.args.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(browseStub.firstCall.args)); + assert.equal(browseStub.firstCall.args.length, 1); browseStub.firstCall.args[0].should.be.an.Object().with.property('filter'); assert.equal(browseStub.firstCall.args[0].filter, 'author:cameron'); @@ -267,7 +269,8 @@ describe('{{#get}} helper', function () { 'posts', {hash: {filter: 'id:-{{post.id}}'}, data: locals, fn: fn, inverse: inverse} ).then(function () { - browseStub.firstCall.args.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(browseStub.firstCall.args)); + assert.equal(browseStub.firstCall.args.length, 1); browseStub.firstCall.args[0].should.be.an.Object().with.property('filter'); assert.equal(browseStub.firstCall.args[0].filter, 'id:-3'); @@ -281,7 +284,8 @@ describe('{{#get}} helper', function () { 'posts', {hash: {filter: 'tags:{{post.tags.[0].slug}}'}, data: locals, fn: fn, inverse: inverse} ).then(function () { - browseStub.firstCall.args.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(browseStub.firstCall.args)); + assert.equal(browseStub.firstCall.args.length, 1); browseStub.firstCall.args[0].should.be.an.Object().with.property('filter'); assert.equal(browseStub.firstCall.args[0].filter, 'tags:test'); @@ -295,7 +299,8 @@ describe('{{#get}} helper', function () { 'posts', {hash: {filter: 'published_at:<=\'{{post.published_at}}\''}, data: locals, fn: fn, inverse: inverse} ).then(function () { - browseStub.firstCall.args.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(browseStub.firstCall.args)); + assert.equal(browseStub.firstCall.args.length, 1); browseStub.firstCall.args[0].should.be.an.Object().with.property('filter'); browseStub.firstCall.args[0].filter.should.eql(`published_at:<='${pubDate.toISOString()}'`); @@ -309,7 +314,8 @@ describe('{{#get}} helper', function () { 'posts', {hash: {filter: 'id:{{post.thing}}'}, data: locals, fn: fn, inverse: inverse} ).then(function () { - browseStub.firstCall.args.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(browseStub.firstCall.args)); + assert.equal(browseStub.firstCall.args.length, 1); browseStub.firstCall.args[0].should.be.an.Object().with.property('filter'); assert.equal(browseStub.firstCall.args[0].filter, 'id:'); @@ -323,7 +329,8 @@ describe('{{#get}} helper', function () { 'posts', {hash: {filter: 'slug:{{@globalProp.foo}}'}, data: locals, fn: fn, inverse: inverse} ).then(function () { - browseStub.firstCall.args.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(browseStub.firstCall.args)); + assert.equal(browseStub.firstCall.args.length, 1); browseStub.firstCall.args[0].should.be.an.Object().with.property('filter'); assert.equal(browseStub.firstCall.args[0].filter, 'slug:bar'); @@ -554,7 +561,8 @@ describe('{{#get}} helper', function () { // The get helper will return as per usual assert.equal(fn.calledOnce, true); fn.firstCall.args[0].should.be.an.Object().with.property('posts'); - fn.firstCall.args[0].posts.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(fn.firstCall.args[0].posts)); + assert.equal(fn.firstCall.args[0].posts.length, 1); }); it('should log an error and return safely if it hits the timeout threshold', async function () { @@ -572,7 +580,7 @@ describe('{{#get}} helper', function () { // The get helper gets called with an empty array of results assert.equal(fn.calledOnce, true); fn.firstCall.args[0].should.be.an.Object().with.property('posts'); - fn.firstCall.args[0].posts.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(fn.firstCall.args[0].posts, []); }); }); }); diff --git a/ghost/core/test/unit/frontend/services/assets-minification/minifier.test.js b/ghost/core/test/unit/frontend/services/assets-minification/minifier.test.js index 45d7ef3703d..483dcfcf1ac 100644 --- a/ghost/core/test/unit/frontend/services/assets-minification/minifier.test.js +++ b/ghost/core/test/unit/frontend/services/assets-minification/minifier.test.js @@ -28,7 +28,8 @@ describe('Minifier', function () { it('star glob e.g. css/*.css', async function () { let result = await minifier.getMatchingFiles('css/*.css'); - result.should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(result)); + assert.equal(result.length, 3); result[0].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','bookmark.css')); result[1].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','empty.css')); result[2].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','gallery.css')); @@ -37,7 +38,8 @@ describe('Minifier', function () { it('match glob range e.g. css/bookmark.css and css/empty.css (css/@(bookmark|empty).css)', async function () { let result = await minifier.getMatchingFiles('css/@(bookmark|empty).css'); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); result[0].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','bookmark.css')); result[1].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','empty.css')); }); @@ -45,14 +47,16 @@ describe('Minifier', function () { it('reverse match glob e.g. css/!(bookmark).css', async function () { let result = await minifier.getMatchingFiles('css/!(bookmark).css'); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); result[0].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','empty.css')); result[1].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','gallery.css')); }); it('reverse match glob e.g. css/!(bookmark|gallery).css', async function () { let result = await minifier.getMatchingFiles('css/!(bookmark|gallery).css'); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); result[0].should.eql(path.join('test','unit','frontend','services','assets-minification','fixtures','basic-cards','css','empty.css')); }); }); @@ -62,14 +66,16 @@ describe('Minifier', function () { let result = await minifier.minify({ 'card.min.js': 'js/*.js' }); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); }); it('single type, multi file', async function () { let result = await minifier.minify({ 'card.min.css': 'css/*.css' }); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); }); it('both css and js types + multiple files', async function () { @@ -78,7 +84,8 @@ describe('Minifier', function () { 'card.min.css': 'css/*.css' }); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); }); it('can replace the content', async function () { @@ -89,7 +96,8 @@ describe('Minifier', function () { '.kg-gallery-image': 'randomword' } }); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); const outputPath = minifier.getFullDest(result[0]); const content = await fs.readFile(outputPath, {encoding: 'utf8'}); @@ -121,7 +129,7 @@ describe('Minifier', function () { await minifier.minify({ 'card.min.ts': 'js/*.ts' }); - should.fail(minifier, 'Should have errored'); + assert.fail('Should have errored'); } catch (err) { assertExists(err); assert.equal(err.errorType, 'IncorrectUsageError'); @@ -135,7 +143,7 @@ describe('Minifier', function () { 'card.min.ts': 'ts/*.ts', 'card.min.js': 'js/fake.js' }); - should.fail(minifier, 'Should have errored'); + assert.fail('Should have errored'); } catch (err) { assertExists(err); assert.equal(err.errorType, 'IncorrectUsageError'); @@ -148,7 +156,7 @@ describe('Minifier', function () { 'card.min.js': 'js/empty.js' }); - result.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result, []); }); it('can minify empty css correctly to no result', async function () { @@ -156,7 +164,7 @@ describe('Minifier', function () { 'card.min.css': 'css/empty.css' }); - result.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result, []); }); }); }); diff --git a/ghost/core/test/unit/frontend/services/rendering/context.test.js b/ghost/core/test/unit/frontend/services/rendering/context.test.js index 010c8a248e6..8b2e880610c 100644 --- a/ghost/core/test/unit/frontend/services/rendering/context.test.js +++ b/ghost/core/test/unit/frontend/services/rendering/context.test.js @@ -37,14 +37,14 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(res.locals.context, []); }); it('should return empty array with no error with basic parameters', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(res.locals.context, []); }); }); @@ -56,7 +56,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'index'); }); @@ -69,7 +70,8 @@ describe('Contexts', function () { // Check context assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 2); assert.equal(res.locals.context[0], 'home'); assert.equal(res.locals.context[1], 'index'); }); @@ -83,7 +85,8 @@ describe('Contexts', function () { // Check context assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'home'); }); @@ -94,7 +97,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'index'); }); @@ -106,7 +110,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 2); assert.equal(res.locals.context[0], 'paged'); assert.equal(res.locals.context[1], 'index'); }); @@ -120,7 +125,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'tag'); }); @@ -131,7 +137,7 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(res.locals.context, []); }); it('will not identify /page/2/ as paged without page param', function () { @@ -141,7 +147,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'tag'); }); @@ -153,7 +160,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 2); assert.equal(res.locals.context[0], 'paged'); assert.equal(res.locals.context[1], 'tag'); }); @@ -167,7 +175,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'author'); }); @@ -178,7 +187,7 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(res.locals.context, []); }); it('will not identify /page/2/ as paged without page param', function () { @@ -188,7 +197,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'author'); }); @@ -200,7 +210,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 2); assert.equal(res.locals.context[0], 'paged'); assert.equal(res.locals.context[1], 'author'); }); @@ -214,7 +225,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 2); assert.equal(res.locals.context[0], 'custom-context'); assert.equal(res.locals.context[1], 'test'); }); @@ -228,7 +240,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'post'); }); }); @@ -241,7 +254,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'private'); }); }); @@ -256,7 +270,8 @@ describe('Contexts', function () { renderer.context(req, res, data); assertExists(res.locals.context); - res.locals.context.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.locals.context)); + assert.equal(res.locals.context.length, 1); assert.equal(res.locals.context[0], 'post'); }); }); diff --git a/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js b/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js index da25b3eba2e..d24023bec7f 100644 --- a/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js +++ b/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js @@ -1,4 +1,4 @@ -const should = require('should'); +const assert = require('node:assert/strict'); const _ = require('lodash'); const hbs = require('../../../../../../core/frontend/services/theme-engine/engine'); @@ -29,8 +29,8 @@ describe('Helpers', function () { const missingHelpers = _.difference(expectedHelpers, foundHelpers); const unexpectedHelpers = _.difference(foundHelpers, expectedHelpers); - missingHelpers.should.be.an.Array().with.lengthOf(0); - unexpectedHelpers.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(missingHelpers, []); + assert.deepEqual(unexpectedHelpers, []); }); }); }); diff --git a/ghost/core/test/unit/server/data/importer/importers/data/posts.test.js b/ghost/core/test/unit/server/data/importer/importers/data/posts.test.js index 601cbafcbc8..4d76c582b9d 100644 --- a/ghost/core/test/unit/server/data/importer/importers/data/posts.test.js +++ b/ghost/core/test/unit/server/data/importer/importers/data/posts.test.js @@ -27,25 +27,25 @@ describe('PostsImporter', function () { const pageFalse = find(importer.dataToImport, {slug: 'page-false'}); assertExists(pageFalse); - should.not.exist(pageFalse.page, 'pageFalse.page should not exist'); + assert.equal(pageFalse.page, undefined, 'pageFalse.page should not exist'); assertExists(pageFalse.type, 'pageFalse.type should exist'); assert.equal(pageFalse.type, 'post'); const pageTrue = find(importer.dataToImport, {slug: 'page-true'}); assertExists(pageTrue); - should.not.exist(pageTrue.page, 'pageTrue.page should not exist'); + assert.equal(pageTrue.page, undefined, 'pageTrue.page should not exist'); assertExists(pageTrue.type, 'pageTrue.type should exist'); assert.equal(pageTrue.type, 'page'); const typePost = find(importer.dataToImport, {slug: 'type-post'}); assertExists(typePost); - should.not.exist(typePost.page, 'typePost.page should not exist'); + assert.equal(typePost.page, undefined, 'typePost.page should not exist'); assertExists(typePost.type, 'typePost.type should exist'); assert.equal(typePost.type, 'post'); const typePage = find(importer.dataToImport, {slug: 'type-page'}); assertExists(typePage); - should.not.exist(typePage.page, 'typePage.page should not exist'); + assert.equal(typePage.page, undefined, 'typePage.page should not exist'); assertExists(typePage.type, 'typePage.type should exist'); assert.equal(typePage.type, 'page'); }); diff --git a/ghost/core/test/unit/server/data/importer/index.test.js b/ghost/core/test/unit/server/data/importer/index.test.js index 8d071c60143..24c58668f3f 100644 --- a/ghost/core/test/unit/server/data/importer/index.test.js +++ b/ghost/core/test/unit/server/data/importer/index.test.js @@ -1,7 +1,6 @@ const assert = require('node:assert/strict'); const {assertExists} = require('../../../../utils/assertions'); const errors = require('@tryghost/errors'); -const should = require('should'); const sinon = require('sinon'); const rewire = require('rewire'); const _ = require('lodash'); @@ -235,7 +234,7 @@ describe('Importer', function () { // They should both have data keys, and they should be equivalent assert('data' in zipResult); assert('data' in fileResult); - zipResult.should.eql(fileResult); + assert.deepEqual(zipResult, fileResult); done(); }); }).catch(done); @@ -398,7 +397,7 @@ describe('Importer', function () { it('throws invalidZipFileBaseDirectory', function () { const testDir = path.resolve('test/utils/fixtures/import/zips/zip-empty'); - should(() => ImportManager.getBaseDirectory(testDir)).throwError(/invalid zip file/i); + assert.throws(() => ImportManager.getBaseDirectory(testDir), /invalid zip file/i); }); }); @@ -445,7 +444,7 @@ describe('Importer', function () { assert.equal(imageSpy.calledWith(inputCopy), true); // eql checks for equality // equal checks the references are for the same object - output.should.not.equal(input); + assert.notEqual(output, input); assert.equal(output.preProcessedByData, true); assert.equal(output.preProcessedByImage, true); assert.equal(output.preProcessedByMedia, true); @@ -483,8 +482,8 @@ describe('Importer', function () { // equal checks the references are for the same object assert.equal(dataSpy.calledOnce, true); assert.equal(imageSpy.calledOnce, true); - dataSpy.getCall(0).args[0].should.eql(expectedData); - imageSpy.getCall(0).args[0].should.eql(expectedImages); + assert.deepEqual(dataSpy.getCall(0).args[0], expectedData); + assert.deepEqual(imageSpy.getCall(0).args[0], expectedImages); // we stubbed this as a noop but ImportManager calls with sequence, so we should get an array assert.deepEqual(output, {images: expectedImages, data: expectedData}); @@ -671,7 +670,7 @@ describe('Importer', function () { }]; MarkdownHandler.loadFile(file).then(function (result) { - result.data.posts.should.be.empty(); + assert.equal(result.data.posts.length, 0); done(); }).catch(done); @@ -732,9 +731,9 @@ describe('Importer', function () { const outputData = DataImporter.preProcess(_.cloneDeep(inputData)); // Data preprocess is a noop - inputData.data.data.posts[0].should.eql(outputData.data.data.posts[0]); - inputData.data.data.tags[0].should.eql(outputData.data.data.tags[0]); - inputData.data.data.users[0].should.eql(outputData.data.data.users[0]); + assert.deepEqual(inputData.data.data.posts[0], outputData.data.data.posts[0]); + assert.deepEqual(inputData.data.data.tags[0], outputData.data.data.tags[0]); + assert.deepEqual(inputData.data.data.users[0], outputData.data.data.users[0]); }); }); }); diff --git a/ghost/core/test/unit/server/data/migrations/utils.test.js b/ghost/core/test/unit/server/data/migrations/utils.test.js index 416056cdbf0..104d784fbb2 100644 --- a/ghost/core/test/unit/server/data/migrations/utils.test.js +++ b/ghost/core/test/unit/server/data/migrations/utils.test.js @@ -34,13 +34,13 @@ describe('migrations/utils', function () { const upResult = migration.up(config); const downResult = migration.down(config); - should.ok(migration.config.transaction, 'Migration config should set transaction to true'); + assert(migration.config.transaction, 'Migration config should set transaction to true'); - should.ok(upResult instanceof Promise, 'Migration up method should return a Promise'); - should.ok(downResult instanceof Promise, 'Migration down method should return a Promise'); + assert(upResult instanceof Promise, 'Migration up method should return a Promise'); + assert(downResult instanceof Promise, 'Migration down method should return a Promise'); - should.ok(up.calledOnceWith(config.transacting), 'up function should be called with transacting'); - should.ok(down.calledOnceWith(config.transacting), 'down function should be called with transacting'); + assert(up.calledOnceWith(config.transacting), 'up function should be called with transacting'); + assert(down.calledOnceWith(config.transacting), 'down function should be called with transacting'); }); }); @@ -58,23 +58,23 @@ describe('migrations/utils', function () { const migrationB = utils.createTransactionalMigration(upB, downB); const combinedMigration = utils.combineTransactionalMigrations(migrationA, migrationB); - should.ok(combinedMigration.config.transaction, 'Migration config should set transaction to true'); + assert(combinedMigration.config.transaction, 'Migration config should set transaction to true'); const upResult = combinedMigration.up(config); - should.ok(upResult instanceof Promise, 'Migration up method should return a Promise'); + assert(upResult instanceof Promise, 'Migration up method should return a Promise'); await upResult; - should.ok(upA.calledOnceWith(config.transacting), 'first up fn should be called with transacting'); - should.ok(upB.calledOnceWith(config.transacting), 'second up fn should be called with transacting'); - should.ok(upA.calledBefore(upB), 'Migration up method should call child up methods in order'); + assert(upA.calledOnceWith(config.transacting), 'first up fn should be called with transacting'); + assert(upB.calledOnceWith(config.transacting), 'second up fn should be called with transacting'); + assert(upA.calledBefore(upB), 'Migration up method should call child up methods in order'); const downResult = combinedMigration.down(config); - should.ok(downResult instanceof Promise, 'Migration down method should return a Promise'); + assert(downResult instanceof Promise, 'Migration down method should return a Promise'); await downResult; - should.ok(downA.calledOnceWith(config.transacting), 'first down fn should be called with transacting'); - should.ok(downB.calledOnceWith(config.transacting), 'second down fn should be called with transacting'); - should.ok(downB.calledBefore(downA), 'Migration down method should call child down methods in reverse order'); + assert(downA.calledOnceWith(config.transacting), 'first down fn should be called with transacting'); + assert(downB.calledOnceWith(config.transacting), 'second down fn should be called with transacting'); + assert(downB.calledBefore(downA), 'Migration down method should call child down methods in reverse order'); }); it('Waits for each migration to finish before executing the next', async function () { @@ -97,15 +97,15 @@ describe('migrations/utils', function () { combinedMigration.up(config); - should.ok(!upB.called, 'second up fn should not be called until first up fn has resolved'); + assert(!upB.called, 'second up fn should not be called until first up fn has resolved'); await upDeferredA.resolve(); - should.ok(upB.calledOnce, 'second up fn should be called once first up fn has resolved'); + assert(upB.calledOnce, 'second up fn should be called once first up fn has resolved'); combinedMigration.down(config); - should.ok(!downA.called, 'penultimate down fn should not be called until last down fn has resolved'); + assert(!downA.called, 'penultimate down fn should not be called until last down fn has resolved'); await downDeferredB.resolve(); - should.ok(downA.calledOnce, 'penultimate down fn should be called once last down fn has resolved'); + assert(downA.calledOnce, 'penultimate down fn should be called once last down fn has resolved'); }); it('Stops execution if a migration errors, and propagates error', async function () { @@ -128,13 +128,13 @@ describe('migrations/utils', function () { const upResult = combinedMigration.up(config); - should.ok(!upB.called, 'second up fn should not be called until first up fn has resolved'); + assert(!upB.called, 'second up fn should not be called until first up fn has resolved'); try { await upDeferredA.reject(new Error('some reason')); } catch (err) { // } finally { - should.ok(!upB.called, 'second up fn should not be called if first up fn has rejected'); + assert(!upB.called, 'second up fn should not be called if first up fn has rejected'); } let upError = null; @@ -143,18 +143,18 @@ describe('migrations/utils', function () { } catch (err) { upError = true; } finally { - should.ok(upError, 'Migration up should error if child errors'); + assert(upError, 'Migration up should error if child errors'); } const downResult = combinedMigration.down(config); - should.ok(!downA.called, 'penultimate down fn should not be called until last down fn has resolved'); + assert(!downA.called, 'penultimate down fn should not be called until last down fn has resolved'); try { await downDeferredB.reject(new Error('some reason')); } catch (err) { // } finally { - should.ok(!downA.called, 'penultimate down fn should not be called if last down fn has rejected'); + assert(!downA.called, 'penultimate down fn should not be called if last down fn has rejected'); } let downErr = null; @@ -163,7 +163,7 @@ describe('migrations/utils', function () { } catch (err) { downErr = err; } finally { - should.ok(downErr, 'Migration down should error if child errors'); + assert(downErr, 'Migration down should error if child errors'); } }); }); @@ -299,7 +299,7 @@ describe('migrations/utils/permissions', function () { action: 'say hello' }); - should.ok(migration.config.transaction, 'addPermission creates a transactional migration'); + assert(migration.config.transaction, 'addPermission creates a transactional migration'); const runDownMigration = await runUpMigration(knex, migration); @@ -308,7 +308,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(insertedPermissionsAfterUp.length === 1, 'The permission was inserted into the database'); + assert(insertedPermissionsAfterUp.length === 1, 'The permission was inserted into the database'); await runDownMigration(); @@ -317,7 +317,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(insertedPermissionsAfterDown.length === 0, 'The permission was removed'); + assert(insertedPermissionsAfterDown.length === 0, 'The permission was removed'); }); }); @@ -353,7 +353,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'Permission Name'; }); - should.ok(attachedPermissionAfterUp, 'The permission was attached to the role.'); + assert(attachedPermissionAfterUp, 'The permission was attached to the role.'); await runDownMigration(); @@ -378,7 +378,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'Permission Name'; }); - should.ok(!attachedPermissionAfterDown, 'The permission was removed from the role.'); + assert(!attachedPermissionAfterDown, 'The permission was removed from the role.'); }); describe('Throws errors', function () { @@ -470,7 +470,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(insertedPermissionsAfterUp.length === 1, 'The permission was inserted into the database'); + assert(insertedPermissionsAfterUp.length === 1, 'The permission was inserted into the database'); const allPermissionsForRoleAfterUp = await knex.raw(` SELECT @@ -493,7 +493,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(permissionAttachedToRoleAfterUp, 'The permission was attached to the role.'); + assert(permissionAttachedToRoleAfterUp, 'The permission was attached to the role.'); await knex.raw(` SELECT @@ -516,7 +516,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(permissionAttachedToOtherRoleAfterUp, 'The permission was attached to the other role.'); + assert(permissionAttachedToOtherRoleAfterUp, 'The permission was attached to the other role.'); await runDownMigration(); @@ -525,7 +525,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(insertedPermissionsAfterDown.length === 0, 'The permission was removed from the database'); + assert(insertedPermissionsAfterDown.length === 0, 'The permission was removed from the database'); const allPermissionsForRoleAfterDown = await knex.raw(` SELECT @@ -548,7 +548,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(!permissionAttachedToRoleAfterDown, 'The permission was removed from the role.'); + assert(!permissionAttachedToRoleAfterDown, 'The permission was removed from the role.'); const allPermissionsForOtherRoleAfterDown = await knex.raw(` SELECT @@ -571,7 +571,7 @@ describe('migrations/utils/permissions', function () { return row.name === 'scarface'; }); - should.ok(!permissionAttachedToOtherRoleAfterDown, 'The permission was removed from the other role.'); + assert(!permissionAttachedToOtherRoleAfterDown, 'The permission was removed from the other role.'); }); }); }); @@ -749,7 +749,7 @@ describe('migrations/utils/schema nullable functions', function () { const migration = utils.createSetNullableMigration('test_nullable_migration', 'not_nullable_col'); - should.ok(migration.config.transaction, 'createSetNullableMigration creates a transactional migration'); + assert(migration.config.transaction, 'createSetNullableMigration creates a transactional migration'); // Verify initial state - column should be not nullable const isNullableInitial = await checkColumnNullable('test_nullable_migration', 'not_nullable_col', knex); @@ -784,7 +784,7 @@ describe('migrations/utils/schema nullable functions', function () { try { const runDownMigration = await runUpMigration(knex, migration); - should.ok(logSpy.calledWith(sinon.match('skipping as column is already nullable')), 'Should log skip message'); + assert(logSpy.calledWith(sinon.match('skipping as column is already nullable')), 'Should log skip message'); // Column should still be nullable const isNullableAfter = await checkColumnNullable('test_nullable_migration', 'nullable_col', knex); @@ -805,7 +805,7 @@ describe('migrations/utils/schema nullable functions', function () { const migration = utils.createDropNullableMigration('test_nullable_migration', 'nullable_col'); - should.ok(migration.config.transaction, 'createDropNullableMigration creates a transactional migration'); + assert(migration.config.transaction, 'createDropNullableMigration creates a transactional migration'); // Verify initial state - column should be nullable const isNullableInitial = await checkColumnNullable('test_nullable_migration', 'nullable_col', knex); @@ -840,7 +840,7 @@ describe('migrations/utils/schema nullable functions', function () { try { const runDownMigration = await runUpMigration(knex, migration); - should.ok(logSpy.calledWith(sinon.match('skipping as column is already not nullable')), 'Should log skip message'); + assert(logSpy.calledWith(sinon.match('skipping as column is already not nullable')), 'Should log skip message'); // Column should still be not nullable const isNotNullableAfter = await checkColumnNullable('test_nullable_migration', 'not_nullable_col', knex); diff --git a/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js b/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js index 14e192547ac..d608054e296 100644 --- a/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js +++ b/ghost/core/test/unit/server/data/schema/fixtures/fixture-manager.test.js @@ -527,7 +527,8 @@ describe('Migration Fixture Utils', function () { it('should fetch a fixture with multiple entries', function () { const foundFixture = fixtureManager.findModelFixtures('Permission', {object_type: 'db'}); assert(_.isPlainObject(foundFixture)); - foundFixture.entries.should.be.an.Array().with.lengthOf(4); + assert(Array.isArray(foundFixture.entries)); + assert.equal(foundFixture.entries.length, 4); assert.deepEqual(foundFixture.entries[0], { name: 'Export database', action_type: 'exportContent', diff --git a/ghost/core/test/unit/server/lib/package-json/filter.test.js b/ghost/core/test/unit/server/lib/package-json/filter.test.js index 1d8b6e6ee40..dc5678c39ad 100644 --- a/ghost/core/test/unit/server/lib/package-json/filter.test.js +++ b/ghost/core/test/unit/server/lib/package-json/filter.test.js @@ -44,11 +44,13 @@ describe('package-json filter', function () { const result = packageJSON.filter({casper: casper}); let package1; - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); package1 = result[0]; package1.should.be.an.Object().with.properties('name', 'package', 'active'); - Object.keys(package1).should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(Object.keys(package1))); + assert.equal(Object.keys(package1).length, 3); assert.equal(package1.name, 'casper'); package1.package.should.be.an.Object().with.properties('name', 'version'); assert.equal(package1.active, false); @@ -59,18 +61,21 @@ describe('package-json filter', function () { let package1; let package2; - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); package1 = result[0]; package2 = result[1]; package1.should.be.an.Object().with.properties('name', 'package', 'active'); - Object.keys(package1).should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(Object.keys(package1))); + assert.equal(Object.keys(package1).length, 3); assert.equal(package1.name, 'casper'); package1.package.should.be.an.Object().with.properties('name', 'version'); assert.equal(package1.active, true); package2.should.be.an.Object().with.properties('name', 'package', 'active'); - Object.keys(package2).should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(Object.keys(package2))); + assert.equal(Object.keys(package2).length, 3); assert.equal(package2.name, 'simple'); package2.package.should.be.an.Object().with.properties('name', 'version'); assert.equal(package2.active, false); @@ -81,18 +86,21 @@ describe('package-json filter', function () { let package1; let package2; - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); package1 = result[0]; package2 = result[1]; package1.should.be.an.Object().with.properties('name', 'package', 'active'); - Object.keys(package1).should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(Object.keys(package1))); + assert.equal(Object.keys(package1).length, 3); assert.equal(package1.name, 'casper'); package1.package.should.be.an.Object().with.properties('name', 'version'); assert.equal(package1.active, true); package2.should.be.an.Object().with.properties('name', 'package', 'active'); - Object.keys(package2).should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(Object.keys(package2))); + assert.equal(Object.keys(package2).length, 3); assert.equal(package2.name, 'simple'); package2.package.should.be.an.Object().with.properties('name', 'version'); assert.equal(package2.active, true); @@ -103,18 +111,21 @@ describe('package-json filter', function () { let package1; let package2; - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); package1 = result[0]; package2 = result[1]; package1.should.be.an.Object().with.properties('name', 'package', 'active'); - Object.keys(package1).should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(Object.keys(package1))); + assert.equal(Object.keys(package1).length, 3); assert.equal(package1.name, 'casper'); package1.package.should.be.an.Object().with.properties('name', 'version'); assert.equal(package1.active, true); package2.should.be.an.Object().with.properties('name', 'package', 'active'); - Object.keys(package2).should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(Object.keys(package2))); + assert.equal(Object.keys(package2).length, 3); assert.equal(package2.name, 'missing'); assert.equal(package2.package, false); assert.equal(package2.active, false); @@ -124,6 +135,6 @@ describe('package-json filter', function () { const result = packageJSON.filter({ '.git': {}, '.anything': {}, 'README.md': {}, _messages: {} }); - result.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result, []); }); }); diff --git a/ghost/core/test/unit/server/models/settings.test.js b/ghost/core/test/unit/server/models/settings.test.js index 90173e3c0db..081de3c5e85 100644 --- a/ghost/core/test/unit/server/models/settings.test.js +++ b/ghost/core/test/unit/server/models/settings.test.js @@ -166,7 +166,7 @@ describe('Unit: models/settings', function () { error = err; } finally { assertExists(error, `Setting Model should throw when saving invalid ${key}`); - should.ok(error instanceof errors.ValidationError, 'Setting Model should throw ValidationError'); + assert(error instanceof errors.ValidationError, 'Setting Model should throw ValidationError'); } } @@ -181,17 +181,11 @@ describe('Unit: models/settings', function () { const setting = models.Settings.forge({key, value, type, group}); - let error; - try { - await setting.save(); - error = null; - } catch (err) { - error = err; - } finally { - tracker.uninstall(); - mockDb.unmock(knex); - should.not.exist(error, `Setting Model should not throw when saving valid ${key}`); - } + // This should not reject. + await setting.save(); + + tracker.uninstall(); + mockDb.unmock(knex); } it('throws when stripe_secret_key is invalid', async function () { diff --git a/ghost/core/test/unit/server/models/user.test.js b/ghost/core/test/unit/server/models/user.test.js index bfca0e4e2d8..79111f98da5 100644 --- a/ghost/core/test/unit/server/models/user.test.js +++ b/ghost/core/test/unit/server/models/user.test.js @@ -69,7 +69,7 @@ describe('Unit: models/user', function () { throw new Error('expected ValidationError'); }) .catch(function (err) { - err.should.be.an.Array(); + assert(Array.isArray(err)); assert.equal((err[0] instanceof errors.ValidationError), true); assert.match(err[0].message, /users\.email/); }); diff --git a/ghost/core/test/unit/server/services/auth/session/session-service.test.js b/ghost/core/test/unit/server/services/auth/session/session-service.test.js index d18d6e83793..f1ca32c3773 100644 --- a/ghost/core/test/unit/server/services/auth/session/session-service.test.js +++ b/ghost/core/test/unit/server/services/auth/session/session-service.test.js @@ -457,7 +457,7 @@ describe('SessionService', function () { }); const authCodeSecond = await sessionServiceSecond.generateAuthCodeForUser(req, res); - should.notEqual(authCodeFirst, authCodeSecond); + assert.notEqual(authCodeFirst, authCodeSecond); }); it('sends an email with the auth code', async function () { diff --git a/ghost/core/test/unit/server/services/lib/dynamic-redirect-manager.test.js b/ghost/core/test/unit/server/services/lib/dynamic-redirect-manager.test.js index 7a0663d2fb0..fa2afdd1ef6 100644 --- a/ghost/core/test/unit/server/services/lib/dynamic-redirect-manager.test.js +++ b/ghost/core/test/unit/server/services/lib/dynamic-redirect-manager.test.js @@ -54,7 +54,7 @@ describe('DynamicRedirectManager', function () { req.url = '/test-params/?q=123&lang=js'; manager.handleRequest(req, res, function next() { - should.fail(true, false, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); assert.equal(headers['Cache-Control'], 'public, max-age=100'); @@ -69,7 +69,7 @@ describe('DynamicRedirectManager', function () { req.url = '/test-params/?q=123&lang=js'; manager.handleRequest(req, res, function next() { - should.ok(true, 'next should have been called'); + assert(true, 'next should have been called'); }); assert.equal(headers, null); @@ -85,7 +85,7 @@ describe('DynamicRedirectManager', function () { req.url = '/test-params/?q=123&lang=js'; manager.handleRequest(req, res, function next() { - should.ok(true, 'next should have been called'); + assert(true, 'next should have been called'); }); assert.equal(headers, null); @@ -104,7 +104,7 @@ describe('DynamicRedirectManager', function () { req.url = '/test-params/'; manager.handleRequest(req, res, function next() { - should.ok(true, 'next should have been called'); + assert(true, 'next should have been called'); }); assert.equal(headers, null); @@ -123,7 +123,7 @@ describe('DynamicRedirectManager', function () { try { manager.addRedirect(from , to); - should.fail(false, 'Should have thrown an error'); + assert.fail('Should have thrown an error'); } catch (e) { assert.equal(e.message, 'Unknown error'); } @@ -141,7 +141,7 @@ describe('DynamicRedirectManager', function () { manager.redirects.should.be.empty(); manager.handleRequest(req, res, function next() { - should.ok(true, 'next should have been called'); + assert(true, 'next should have been called'); }); }); @@ -155,7 +155,7 @@ describe('DynamicRedirectManager', function () { req.url = '/post/10/a-nice-blog-post'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -173,7 +173,7 @@ describe('DynamicRedirectManager', function () { req.url = '/post/10/a-nice-blog-post/'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -191,7 +191,7 @@ describe('DynamicRedirectManager', function () { req.url = '/post/10/a-nice-blog-post?a=b'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -209,7 +209,7 @@ describe('DynamicRedirectManager', function () { req.url = '/topic?something=good'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -229,7 +229,7 @@ describe('DynamicRedirectManager', function () { req.url = '/CaSe-InSeNsItIvE'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -247,7 +247,7 @@ describe('DynamicRedirectManager', function () { req.url = '/Case-Sensitive'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -265,7 +265,7 @@ describe('DynamicRedirectManager', function () { req.url = '/Default-Sensitive'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); assert.equal(headers['Cache-Control'], 'public, max-age=0'); @@ -282,7 +282,7 @@ describe('DynamicRedirectManager', function () { req.url = '/casE-sensitivE'; manager.handleRequest(req, res, function next() { - should.ok(true, 'next should have been called'); + assert(true, 'next should have been called'); }); assert.equal(headers, null); @@ -299,7 +299,7 @@ describe('DynamicRedirectManager', function () { req.url = '/defaulT-sensitivE'; manager.handleRequest(req, res, function next() { - should.ok(true, 'next should have been called'); + assert(true, 'next should have been called'); }); assert.equal(headers, null); @@ -318,7 +318,7 @@ describe('DynamicRedirectManager', function () { req.url = '/external-url/'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -336,7 +336,7 @@ describe('DynamicRedirectManager', function () { req.url = '/external-url'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -354,7 +354,7 @@ describe('DynamicRedirectManager', function () { req.url = '/external-url/docs'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -374,7 +374,7 @@ describe('DynamicRedirectManager', function () { req.url = from; manager.handleRequest(req, res, function next() { - should.fail(true, false, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); // NOTE: max-age is "0" because it's not a permanent redirect @@ -406,7 +406,7 @@ describe('DynamicRedirectManager', function () { req.url = '/blog/my-old-blog-post/'; manager.handleRequest(req, res, function next() { - should.fail(true, 'next should NOT have been called'); + assert.fail('next should NOT have been called'); }); assert.equal(headers['Cache-Control'], 'public, max-age=100'); diff --git a/ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js b/ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js index 02a6f3b2dd9..298fb16a7b8 100644 --- a/ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js +++ b/ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js @@ -1,6 +1,5 @@ const LinkClickTrackingService = require('../../../../../core/server/services/link-tracking/link-click-tracking-service'); const sinon = require('sinon'); -const should = require('should'); const assert = require('node:assert/strict'); const ObjectID = require('bson-objectid').default; const PostLink = require('../../../../../core/server/services/link-tracking/post-link'); @@ -343,7 +342,10 @@ describe('LinkClickTrackingService', function () { } }; - await should(service.bulkEdit(data, options)).be.rejectedWith(errors.BadRequestError); + await assert.rejects( + service.bulkEdit(data, options), + errors.BadRequestError + ); }); }); }); diff --git a/ghost/core/test/unit/server/services/mail/ghost-mailer.test.js b/ghost/core/test/unit/server/services/mail/ghost-mailer.test.js index ce9ff59e2cf..e0964aaa3a1 100644 --- a/ghost/core/test/unit/server/services/mail/ghost-mailer.test.js +++ b/ghost/core/test/unit/server/services/mail/ghost-mailer.test.js @@ -351,7 +351,7 @@ describe('Mail: Ghostmailer', function () { }); const sentMessage = sendMailSpy.firstCall.args[0]; - sentMessage['o:tag'].should.be.an.Array(); + assert(Array.isArray(sentMessage['o:tag'])); assert(sentMessage['o:tag'].includes('transactional-email')); assert(sentMessage['o:tag'].includes('blog-123123')); assert.equal(sentMessage['o:tracking-opens'], true); @@ -374,7 +374,7 @@ describe('Mail: Ghostmailer', function () { }); const sentMessage = sendMailSpy.firstCall.args[0]; - sentMessage['o:tag'].should.be.an.Array(); + assert(Array.isArray(sentMessage['o:tag'])); assert(sentMessage['o:tag'].includes('transactional-email')); assert(sentMessage['o:tag'].includes('blog-123123')); assert.equal(sentMessage['o:tracking-opens'], undefined); diff --git a/ghost/core/test/unit/server/services/member-attribution/history.test.js b/ghost/core/test/unit/server/services/member-attribution/history.test.js index c989dd42b7f..993ba55ecd4 100644 --- a/ghost/core/test/unit/server/services/member-attribution/history.test.js +++ b/ghost/core/test/unit/server/services/member-attribution/history.test.js @@ -1,5 +1,4 @@ const assert = require('node:assert/strict'); -const should = require('should'); const UrlHistory = require('../../../../../core/server/services/member-attribution/url-history'); @@ -95,7 +94,7 @@ describe('UrlHistory', function () { ]; for (const input of inputs) { const history = UrlHistory.create(input); - should(history.history).eql(input); + assert.deepEqual(history.history, input); } }); diff --git a/ghost/core/test/unit/server/services/member-attribution/service.test.js b/ghost/core/test/unit/server/services/member-attribution/service.test.js index 0d24a7c2987..82c9436c7f7 100644 --- a/ghost/core/test/unit/server/services/member-attribution/service.test.js +++ b/ghost/core/test/unit/server/services/member-attribution/service.test.js @@ -1,5 +1,4 @@ const assert = require('node:assert/strict'); -const should = require('should'); const MemberAttributionService = require('../../../../../core/server/services/member-attribution/member-attribution-service'); @@ -216,7 +215,7 @@ describe('MemberAttributionService', function () { const service = new MemberAttributionService({ attributionBuilder: { getAttribution: async function (history) { - should(history).have.property('length'); + assert('length' in history); return {success: true}; } }, diff --git a/ghost/core/test/unit/server/services/members/middleware.test.js b/ghost/core/test/unit/server/services/members/middleware.test.js index ce091207fa0..b1cc2feb5d7 100644 --- a/ghost/core/test/unit/server/services/members/middleware.test.js +++ b/ghost/core/test/unit/server/services/members/middleware.test.js @@ -57,7 +57,7 @@ describe('Members Service Middleware', function () { // Check behavior assert.equal(next.calledOnce, true); - next.firstCall.args.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(next.firstCall.args, []); }); it('redirects correctly on success', async function () { diff --git a/ghost/core/test/unit/server/services/members/request-integrity-token-provider.test.js b/ghost/core/test/unit/server/services/members/request-integrity-token-provider.test.js index 72f4c8d7c31..8ba024cd4bb 100644 --- a/ghost/core/test/unit/server/services/members/request-integrity-token-provider.test.js +++ b/ghost/core/test/unit/server/services/members/request-integrity-token-provider.test.js @@ -23,7 +23,8 @@ describe('RequestIntegrityTokenProvider', function () { const token = tokenProvider.create(); token.should.be.a.String(); - token.split(':').should.be.an.Array().with.lengthOf(3); + assert(Array.isArray(token.split(':'))); + assert.equal(token.split(':').length, 3); const [timestamp, nonce, digest] = token.split(':'); assert.equal(timestamp, (new Date('2021-01-01').valueOf() + 100).toString()); diff --git a/ghost/core/test/unit/server/services/members/stripe-connect.test.js b/ghost/core/test/unit/server/services/members/stripe-connect.test.js index f990641523f..fbc04d5c527 100644 --- a/ghost/core/test/unit/server/services/members/stripe-connect.test.js +++ b/ghost/core/test/unit/server/services/members/stripe-connect.test.js @@ -11,7 +11,7 @@ describe('Members - Stripe Connect', function () { /** @type URL */ const url = await stripeConnect.getStripeConnectOAuthUrl(setSessionProp); - should.ok(url instanceof URL, 'getStripeConnectOAuthUrl should return an instance of URL'); + assert(url instanceof URL, 'getStripeConnectOAuthUrl should return an instance of URL'); assertExists(session.get(stripeConnect.STATE_PROP), 'The session should have a state set'); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.js index e43d7c88360..5d5c4748d00 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.js @@ -11,50 +11,35 @@ describe('OfferAmount', function () { OfferPercentageAmount.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferPercentageAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferPercentageAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferPercentageAmount.create('1'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferPercentageAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferPercentageAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferPercentageAmount.create(-1); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferPercentageAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferPercentageAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferPercentageAmount.create(200); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferPercentageAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferPercentageAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferPercentageAmount.create(3.14); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferPercentageAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferPercentageAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } OfferPercentageAmount.create(69); // nice @@ -75,40 +60,28 @@ describe('OfferAmount', function () { OfferFixedAmount.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFixedAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFixedAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferFixedAmount.create('1'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFixedAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFixedAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferFixedAmount.create(-1); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFixedAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFixedAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferFixedAmount.create(3.14); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFixedAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFixedAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } OfferFixedAmount.create(200); @@ -129,40 +102,28 @@ describe('OfferAmount', function () { OfferTrialAmount.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferTrialAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferTrialAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferTrialAmount.create('1'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferTrialAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferTrialAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferTrialAmount.create(-1); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferTrialAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferTrialAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferTrialAmount.create(3.14); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferTrialAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferTrialAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } OfferTrialAmount.create(200); @@ -181,52 +142,37 @@ describe('OfferAmount', function () { it('Will only create an OfferFreeMonthsAmount containing an integer greater than 0', function () { try { OfferFreeMonthsAmount.create(); - should.fail(); + assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferFreeMonthsAmount.create('1'); - should.fail(); + assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferFreeMonthsAmount.create(0); - should.fail(); + assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferFreeMonthsAmount.create(-1); - should.fail(); + assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } try { OfferFreeMonthsAmount.create(3.14); - should.fail(); + assert.fail(); } catch (err) { - should.ok( - err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, - 'expected an InvalidOfferAmount error' - ); + assert(err instanceof OfferFreeMonthsAmount.InvalidOfferAmount, 'expected an InvalidOfferAmount error'); } OfferFreeMonthsAmount.create(1); @@ -236,7 +182,7 @@ describe('OfferAmount', function () { it('Exposes a number on the value property', function () { const cadence = OfferFreeMonthsAmount.create(2); - should.ok(typeof cadence.value === 'number'); + assert.equal(typeof cadence.value, 'number'); }); }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js index ab6a64c5d46..ce79e4085f2 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js @@ -13,30 +13,21 @@ describe('OfferCadence', function () { OfferCadence.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCadence.InvalidOfferCadence, - 'expected an InvalidOfferCadence error' - ); + assert(err instanceof OfferCadence.InvalidOfferCadence, 'expected an InvalidOfferCadence error'); } try { OfferCadence.create(12); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCadence.InvalidOfferCadence, - 'expected an InvalidOfferCadence error' - ); + assert(err instanceof OfferCadence.InvalidOfferCadence, 'expected an InvalidOfferCadence error'); } try { OfferCadence.create('daily'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCadence.InvalidOfferCadence, - 'expected an InvalidOfferCadence error' - ); + assert(err instanceof OfferCadence.InvalidOfferCadence, 'expected an InvalidOfferCadence error'); } }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js index 0f8356da4b4..fb8eeefc808 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js @@ -12,20 +12,14 @@ describe('OfferCode', function () { OfferCode.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCode.InvalidOfferCode, - 'expected an InvalidOfferCode error' - ); + assert(err instanceof OfferCode.InvalidOfferCode, 'expected an InvalidOfferCode error'); } try { OfferCode.create(1234); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCode.InvalidOfferCode, - 'expected an InvalidOfferCode error' - ); + assert(err instanceof OfferCode.InvalidOfferCode, 'expected an InvalidOfferCode error'); } const code = OfferCode.create('Hello, world'); @@ -48,10 +42,7 @@ describe('OfferCode', function () { OfferCode.create(tooLong); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCode.InvalidOfferCode, - 'expected an InvalidOfferCode error' - ); + assert(err instanceof OfferCode.InvalidOfferCode, 'expected an InvalidOfferCode error'); } }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.js index 916f9af8c7f..2b5ca8b266d 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.js @@ -13,50 +13,35 @@ describe('OfferCurrency', function () { OfferCurrency.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCurrency.InvalidOfferCurrency, - 'expected an InvalidOfferCurrency error' - ); + assert(err instanceof OfferCurrency.InvalidOfferCurrency, 'expected an InvalidOfferCurrency error'); } try { OfferCurrency.create('US Dollars'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCurrency.InvalidOfferCurrency, - 'expected an InvalidOfferCurrency error' - ); + assert(err instanceof OfferCurrency.InvalidOfferCurrency, 'expected an InvalidOfferCurrency error'); } try { OfferCurrency.create('$'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCurrency.InvalidOfferCurrency, - 'expected an InvalidOfferCurrency error' - ); + assert(err instanceof OfferCurrency.InvalidOfferCurrency, 'expected an InvalidOfferCurrency error'); } try { OfferCurrency.create('USDC'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCurrency.InvalidOfferCurrency, - 'expected an InvalidOfferCurrency error' - ); + assert(err instanceof OfferCurrency.InvalidOfferCurrency, 'expected an InvalidOfferCurrency error'); } try { OfferCurrency.create(2); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferCurrency.InvalidOfferCurrency, - 'expected an InvalidOfferCurrency error' - ); + assert(err instanceof OfferCurrency.InvalidOfferCurrency, 'expected an InvalidOfferCurrency error'); } }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-description.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-description.test.js index 250897777aa..6e97e1b768a 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-description.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-description.test.js @@ -16,20 +16,14 @@ describe('OfferDescription', function () { OfferDescription.create(12); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDescription.InvalidOfferDescription, - 'expected an InvalidOfferDescription error' - ); + assert(err instanceof OfferDescription.InvalidOfferDescription, 'expected an InvalidOfferDescription error'); } try { OfferDescription.create({}); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDescription.InvalidOfferDescription, - 'expected an InvalidOfferDescription error' - ); + assert(err instanceof OfferDescription.InvalidOfferDescription, 'expected an InvalidOfferDescription error'); } }); @@ -48,10 +42,7 @@ describe('OfferDescription', function () { OfferDescription.create(tooLong); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDescription.InvalidOfferDescription, - 'expected an InvalidOfferDescription error' - ); + assert(err instanceof OfferDescription.InvalidOfferDescription, 'expected an InvalidOfferDescription error'); } }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.js index 67aadd6be3c..e22b8a7f4c6 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.js @@ -16,60 +16,42 @@ describe('OfferDuration', function () { OfferDuration.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDuration.InvalidOfferDuration, - 'expected an InvalidOfferDuration error' - ); + assert(err instanceof OfferDuration.InvalidOfferDuration, 'expected an InvalidOfferDuration error'); } try { OfferDuration.create('other'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDuration.InvalidOfferDuration, - 'expected an InvalidOfferDuration error' - ); + assert(err instanceof OfferDuration.InvalidOfferDuration, 'expected an InvalidOfferDuration error'); } try { OfferDuration.create('repeating'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDuration.InvalidOfferDuration, - 'expected an InvalidOfferDuration error' - ); + assert(err instanceof OfferDuration.InvalidOfferDuration, 'expected an InvalidOfferDuration error'); } try { OfferDuration.create('repeating', 1.5); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDuration.InvalidOfferDuration, - 'expected an InvalidOfferDuration error' - ); + assert(err instanceof OfferDuration.InvalidOfferDuration, 'expected an InvalidOfferDuration error'); } try { OfferDuration.create('repeating', -12); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDuration.InvalidOfferDuration, - 'expected an InvalidOfferDuration error' - ); + assert(err instanceof OfferDuration.InvalidOfferDuration, 'expected an InvalidOfferDuration error'); } try { OfferDuration.create('repeating', '2'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferDuration.InvalidOfferDuration, - 'expected an InvalidOfferDuration error' - ); + assert(err instanceof OfferDuration.InvalidOfferDuration, 'expected an InvalidOfferDuration error'); } }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js index 0168c308849..cbcd4f50f95 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js @@ -12,40 +12,28 @@ describe('OfferName', function () { OfferName.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferName.InvalidOfferName, - 'expected an InvalidOfferName error' - ); + assert(err instanceof OfferName.InvalidOfferName, 'expected an InvalidOfferName error'); } try { OfferName.create(null); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferName.InvalidOfferName, - 'expected an InvalidOfferName error' - ); + assert(err instanceof OfferName.InvalidOfferName, 'expected an InvalidOfferName error'); } try { OfferName.create(12); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferName.InvalidOfferName, - 'expected an InvalidOfferName error' - ); + assert(err instanceof OfferName.InvalidOfferName, 'expected an InvalidOfferName error'); } try { OfferName.create({}); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferName.InvalidOfferName, - 'expected an InvalidOfferName error' - ); + assert(err instanceof OfferName.InvalidOfferName, 'expected an InvalidOfferName error'); } }); @@ -64,10 +52,7 @@ describe('OfferName', function () { OfferName.create(tooLong); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferName.InvalidOfferName, - 'expected an InvalidOfferName error' - ); + assert(err instanceof OfferName.InvalidOfferName, 'expected an InvalidOfferName error'); } }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-redemption-type.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-redemption-type.test.js index 42ecd36586a..79c59e3bea4 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-redemption-type.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-redemption-type.test.js @@ -13,20 +13,14 @@ describe('OfferRedemptionType', function () { OfferRedemptionType.create('other'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferRedemptionType.InvalidOfferRedemptionType, - 'expected an InvalidOfferRedemptionType error' - ); + assert(err instanceof OfferRedemptionType.InvalidOfferRedemptionType, 'expected an InvalidOfferRedemptionType error'); } try { OfferRedemptionType.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferRedemptionType.InvalidOfferRedemptionType, - 'expected an InvalidOfferRedemptionType error' - ); + assert(err instanceof OfferRedemptionType.InvalidOfferRedemptionType, 'expected an InvalidOfferRedemptionType error'); } }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-status.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-status.test.js index 894a136f6fb..5b26c50100e 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-status.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-status.test.js @@ -13,20 +13,14 @@ describe('OfferStatus', function () { OfferStatus.create('other'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferStatus.InvalidOfferStatus, - 'expected an InvalidOfferStatus error' - ); + assert(err instanceof OfferStatus.InvalidOfferStatus, 'expected an InvalidOfferStatus error'); } try { OfferStatus.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferStatus.InvalidOfferStatus, - 'expected an InvalidOfferStatus error' - ); + assert(err instanceof OfferStatus.InvalidOfferStatus, 'expected an InvalidOfferStatus error'); } }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-title.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-title.test.js index cf1926729f5..289df19fdd8 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-title.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-title.test.js @@ -16,20 +16,14 @@ describe('OfferTitle', function () { OfferTitle.create(12); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferTitle.InvalidOfferTitle, - 'expected an InvalidOfferTitle error' - ); + assert(err instanceof OfferTitle.InvalidOfferTitle, 'expected an InvalidOfferTitle error'); } try { OfferTitle.create({}); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferTitle.InvalidOfferTitle, - 'expected an InvalidOfferTitle error' - ); + assert(err instanceof OfferTitle.InvalidOfferTitle, 'expected an InvalidOfferTitle error'); } }); @@ -48,10 +42,7 @@ describe('OfferTitle', function () { OfferTitle.create(tooLong); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferTitle.InvalidOfferTitle, - 'expected an InvalidOfferTitle error' - ); + assert(err instanceof OfferTitle.InvalidOfferTitle, 'expected an InvalidOfferTitle error'); } }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js index a7d73149421..f33bbe91116 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js @@ -15,20 +15,14 @@ describe('OfferType', function () { OfferType.create('other'); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferType.InvalidOfferType, - 'expected an InvalidOfferType error' - ); + assert(err instanceof OfferType.InvalidOfferType, 'expected an InvalidOfferType error'); } try { OfferType.create(); assert.fail(); } catch (err) { - should.ok( - err instanceof OfferType.InvalidOfferType, - 'expected an InvalidOfferType error' - ); + assert(err instanceof OfferType.InvalidOfferType, 'expected an InvalidOfferType error'); } }); }); @@ -57,7 +51,7 @@ describe('OfferType', function () { describe('OfferType.FreeMonths', function () { it('Is an OfferType with a value of "free_months"', function () { assert.equal(OfferType.FreeMonths.value, 'free_months'); - should.ok(OfferType.FreeMonths.equals(OfferType.create('free_months'))); + assert(OfferType.FreeMonths.equals(OfferType.create('free_months'))); }); }); }); diff --git a/ghost/core/test/unit/server/services/offers/domain/models/offer.test.js b/ghost/core/test/unit/server/services/offers/domain/models/offer.test.js index 1b53b9bdc0a..45dc9294a52 100644 --- a/ghost/core/test/unit/server/services/offers/domain/models/offer.test.js +++ b/ghost/core/test/unit/server/services/offers/domain/models/offer.test.js @@ -37,10 +37,7 @@ describe('Offer', function () { id: ObjectID() } }, mockUniqueChecker); - should.ok( - offer instanceof Offer, - 'Offer.create should return an instance of Offer' - ); + assert(offer instanceof Offer, 'Offer.create should return an instance of Offer'); }); it('Stores stripe_coupon_id when provided', async function () { @@ -77,10 +74,7 @@ describe('Offer', function () { id: ObjectID() } }, mockUniqueChecker); - should.ok( - offer instanceof Offer, - 'Offer.create should return an instance of Offer' - ); + assert(offer instanceof Offer, 'Offer.create should return an instance of Offer'); }); it('Can create a free-months offer on monthly cadence', async function () { @@ -99,10 +93,7 @@ describe('Offer', function () { id: ObjectID() } }, mockUniqueChecker); - should.ok( - offer instanceof Offer, - 'Should create a free months offer on monthly cadence' - ); + assert(offer instanceof Offer, 'Should create a free months offer on monthly cadence'); }); it('Can create a free-months offer on yearly cadence', async function () { @@ -121,10 +112,7 @@ describe('Offer', function () { id: ObjectID() } }, mockUniqueChecker); - should.ok( - offer instanceof Offer, - 'Should create a free months offer on yearly cadence' - ); + assert(offer instanceof Offer, 'Should create a free months offer on yearly cadence'); }); it('Throws an error if the duration for trial offer is not right', async function () { @@ -163,9 +151,9 @@ describe('Offer', function () { id: ObjectID() } }, mockUniqueChecker).then(() => { - should.fail('Expected an error'); + assert.fail('Expected an error'); }, (err) => { - should.ok(err); + assert(err); }); }); @@ -405,7 +393,7 @@ describe('Offer', function () { tier: null }, mockUniqueChecker); - should.ok(offer instanceof Offer); + assert(offer instanceof Offer); assert.equal(offer.tier, null); assert.equal(offer.redemptionType.value, 'retention'); }); @@ -427,9 +415,9 @@ describe('Offer', function () { id: ObjectID() } }, mockUniqueChecker); - should.fail('Expected an error'); + assert.fail('Expected an error'); } catch (err) { - should.ok(err instanceof errors.InvalidOfferTier); + assert(err instanceof errors.InvalidOfferTier); } }); @@ -448,9 +436,9 @@ describe('Offer', function () { redemption_type: 'signup', tier: null }, mockUniqueChecker); - should.fail('Expected an error'); + assert.fail('Expected an error'); } catch (err) { - should.ok(err instanceof errors.InvalidOfferTier); + assert(err instanceof errors.InvalidOfferTier); } }); @@ -468,9 +456,9 @@ describe('Offer', function () { duration: 'forever', tier: null }, mockUniqueChecker); - should.fail('Expected an error'); + assert.fail('Expected an error'); } catch (err) { - should.ok(err instanceof errors.InvalidOfferTier); + assert(err instanceof errors.InvalidOfferTier); } }); }); diff --git a/ghost/core/test/unit/server/services/permissions/providers.test.js b/ghost/core/test/unit/server/services/permissions/providers.test.js index 5ea796d911e..5c06e76953b 100644 --- a/ghost/core/test/unit/server/services/permissions/providers.test.js +++ b/ghost/core/test/unit/server/services/permissions/providers.test.js @@ -63,8 +63,10 @@ describe('Permission Providers', function () { res.should.be.an.Object().with.properties('permissions', 'roles'); - res.permissions.should.be.an.Array().with.lengthOf(10); - res.roles.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.permissions)); + assert.equal(res.permissions.length, 10); + assert(Array.isArray(res.roles)); + assert.equal(res.roles.length, 1); // @TODO fix this! // Permissions is an array of models @@ -113,8 +115,10 @@ describe('Permission Providers', function () { res.should.be.an.Object().with.properties('permissions', 'roles'); - res.permissions.should.be.an.Array().with.lengthOf(10); - res.roles.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.permissions)); + assert.equal(res.permissions.length, 10); + assert(Array.isArray(res.roles)); + assert.equal(res.roles.length, 1); // @TODO fix this! // Permissions is an array of models @@ -164,8 +168,10 @@ describe('Permission Providers', function () { res.should.be.an.Object().with.properties('permissions', 'roles'); - res.permissions.should.be.an.Array().with.lengthOf(10); - res.roles.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.permissions)); + assert.equal(res.permissions.length, 10); + assert(Array.isArray(res.roles)); + assert.equal(res.roles.length, 1); // @TODO fix this! // Permissions is an array of models @@ -234,7 +240,8 @@ describe('Permission Providers', function () { providers.apiKey(1).then((res) => { assert.equal(findApiKeySpy.callCount, 1); res.should.be.an.Object().with.properties('permissions', 'roles'); - res.roles.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(res.roles)); + assert.equal(res.roles.length, 1); res.permissions[0].should.be.an.Object().with.properties('attributes', 'id'); res.roles[0].should.be.an.Object().with.properties('id', 'name', 'description'); assert(res.permissions[0] instanceof models.Base.Model); diff --git a/ghost/core/test/unit/server/services/route-settings/route-settings.test.js b/ghost/core/test/unit/server/services/route-settings/route-settings.test.js index 6fa10efd679..b04e7713948 100644 --- a/ghost/core/test/unit/server/services/route-settings/route-settings.test.js +++ b/ghost/core/test/unit/server/services/route-settings/route-settings.test.js @@ -43,7 +43,7 @@ describe('UNIT > Settings Service DefaultSettingsManager:', function () { try { await defaultSettingsManager.setFromFilePath(incomingSettingsPath); - assert.fail('should.fail'); + assert.fail(); } catch (error) { assert.match(error.message, /YAMLException: bad indentation of a mapping entry/); } diff --git a/ghost/core/test/unit/server/services/route-settings/settings-path-manager.test.js b/ghost/core/test/unit/server/services/route-settings/settings-path-manager.test.js index d5f129278f3..7ea1b4c7a55 100644 --- a/ghost/core/test/unit/server/services/route-settings/settings-path-manager.test.js +++ b/ghost/core/test/unit/server/services/route-settings/settings-path-manager.test.js @@ -1,23 +1,14 @@ -// Switch these lines once there are useful utils const assert = require('node:assert/strict'); -const {assertExists} = require('../../../../utils/assertions'); -// const testUtils = require('./utils'); -const should = require('should'); const SettingsPathManager = require('../../../../../core/server/services/route-settings/settings-path-manager'); describe('Settings Path Manager', function () { it('throws when paths parameter is not provided', function () { - try { - const settingsPathManager = new SettingsPathManager({ + assert.throws(() => { + new SettingsPathManager({ paths: [], type: 'routes' }); - - should.fail(settingsPathManager, 'Should have errored'); - } catch (err) { - assertExists(err); - assert.match(err.message, /paths values/g); - } + }, /paths values/g); }); describe('getDefaultFilePath', function () { diff --git a/ghost/core/test/unit/server/services/stats/content.test.js b/ghost/core/test/unit/server/services/stats/content.test.js index 2c4fc72d506..9ff3c8ef4cf 100644 --- a/ghost/core/test/unit/server/services/stats/content.test.js +++ b/ghost/core/test/unit/server/services/stats/content.test.js @@ -102,7 +102,8 @@ describe('ContentStatsService', function () { const result = mockTinybirdClient.parseResponse(mockResponse); assertExists(result); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); assert.equal(mockTinybirdClient.parseResponse.calledWith(mockResponse), true); }); @@ -117,7 +118,8 @@ describe('ContentStatsService', function () { const result = service.extractPostUuids(data); assertExists(result); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); assert(result.includes('post-1')); assert(result.includes('post-2')); }); @@ -131,7 +133,7 @@ describe('ContentStatsService', function () { const result = service.extractPostUuids(data); assertExists(result); - result.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result, []); }); }); @@ -227,7 +229,7 @@ describe('ContentStatsService', function () { it('returns empty array for empty input', async function () { const result = await service.enrichTopContentData([]); assertExists(result); - result.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result, []); assert.equal(service.extractPostUuids.called, false); assert.equal(service.lookupPostTitles.called, false); @@ -246,7 +248,8 @@ describe('ContentStatsService', function () { const result = await service.enrichTopContentData(data); assertExists(result); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); assert.equal(result[0].title, 'Test Post 1'); assert.equal(result[0].post_id, 'post-id-1'); assert.equal(result[0].url_exists, true); @@ -273,7 +276,8 @@ describe('ContentStatsService', function () { const result = await service.enrichTopContentData(data); assertExists(result); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); assert.equal(result[0].title, 'About Us'); assert.equal(result[0].resourceType, 'page'); assert.equal(result[0].url_exists, true); @@ -291,7 +295,8 @@ describe('ContentStatsService', function () { const result = await service.enrichTopContentData(data); assertExists(result); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); assert.equal(result[0].title, 'unknown-page'); assert.equal(result[0].url_exists, false); @@ -308,7 +313,8 @@ describe('ContentStatsService', function () { const result = await service.enrichTopContentData(data); assertExists(result); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); assert.equal(result[0].title, 'Homepage'); assert.equal(result[0].url_exists, false); }); @@ -331,7 +337,8 @@ describe('ContentStatsService', function () { const result = await service.fetchRawTopContentData(options); assertExists(result); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); assert.equal(result[0].pathname, '/test/'); assert.equal(result[0].visits, 100); @@ -389,7 +396,8 @@ describe('ContentStatsService', function () { assertExists(result); assertExists(result.data); - result.data.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result.data)); + assert.equal(result.data.length, 2); assert('title' in result.data[0]); assert('post_id' in result.data[0]); assert('title' in result.data[1]); @@ -413,7 +421,7 @@ describe('ContentStatsService', function () { assertExists(result); assertExists(result.data); - result.data.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result.data, []); assert.equal(service.fetchRawTopContentData.calledOnce, true); }); @@ -428,7 +436,7 @@ describe('ContentStatsService', function () { assertExists(result); assertExists(result.data); - result.data.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result.data, []); }); it('returns empty data array when tinybirdClient is not available', async function () { @@ -445,7 +453,7 @@ describe('ContentStatsService', function () { assertExists(result); assertExists(result.data); - result.data.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result.data, []); }); }); @@ -468,7 +476,8 @@ describe('ContentStatsService', function () { // Verify result is correct assertExists(result); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); // Verify tinybird client was called with correct parameters assert.equal(mockTinybirdClient.fetch.calledOnce, true); @@ -487,7 +496,7 @@ describe('ContentStatsService', function () { const result = await service.getTopContent({}); assertExists(result); - result.should.have.property('data').which.is.an.Array().with.lengthOf(0); + assert.deepEqual(result.data, []); }); it('passes all filter parameters to tinybird client with correct shape', async function () { diff --git a/ghost/core/test/unit/server/services/stats/utils/tinybird.test.js b/ghost/core/test/unit/server/services/stats/utils/tinybird.test.js index af430a67e68..a07807d0ba8 100644 --- a/ghost/core/test/unit/server/services/stats/utils/tinybird.test.js +++ b/ghost/core/test/unit/server/services/stats/utils/tinybird.test.js @@ -133,7 +133,8 @@ describe('Tinybird Client', function () { const result = tinybirdClient.parseResponse(mockResponse); assertExists(result); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); assert.equal(result[0].pathname, '/test-1/'); assert.equal(result[0].visits, 100); }); @@ -150,7 +151,8 @@ describe('Tinybird Client', function () { const result = tinybirdClient.parseResponse(mockResponse); assertExists(result); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); }); it('handles direct JSON string response', function () { @@ -162,7 +164,8 @@ describe('Tinybird Client', function () { const result = tinybirdClient.parseResponse(mockResponse); assertExists(result); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); }); it('handles direct object response', function () { @@ -174,7 +177,8 @@ describe('Tinybird Client', function () { const result = tinybirdClient.parseResponse(mockResponse); assertExists(result); - result.should.be.an.Array().with.lengthOf(1); + assert(Array.isArray(result)); + assert.equal(result.length, 1); }); it('returns empty array for empty data', function () { @@ -184,7 +188,7 @@ describe('Tinybird Client', function () { const result = tinybirdClient.parseResponse(mockResponse); assertExists(result); - result.should.be.an.Array().with.lengthOf(0); + assert.deepEqual(result, []); }); it('returns null for invalid JSON', function () { @@ -215,7 +219,8 @@ describe('Tinybird Client', function () { }); assertExists(result); - result.should.be.an.Array().with.lengthOf(2); + assert(Array.isArray(result)); + assert.equal(result.length, 2); assert.equal(result[0].pathname, '/test-1/'); assert.equal(result[0].visits, 100); diff --git a/ghost/core/test/unit/server/services/themes/validate.test.js b/ghost/core/test/unit/server/services/themes/validate.test.js index 48c073c8dcd..48d55037471 100644 --- a/ghost/core/test/unit/server/services/themes/validate.test.js +++ b/ghost/core/test/unit/server/services/themes/validate.test.js @@ -132,21 +132,18 @@ describe('Themes', function () { }); }); - it('[failure] can handle a corrupt zip file', function () { + it('[failure] can handle a corrupt zip file', async function () { checkZipStub.rejects(new Error('invalid zip file')); formatStub.returns({results: {error: []}}); - return validate.check(testTheme.name, testTheme, {isZip: true}) - .then((checkedTheme) => { - checkedTheme.should.not.exist(); - }).catch((error) => { - assert(error instanceof Error); - assert.equal(error.message, 'invalid zip file'); - assert.equal(checkZipStub.calledOnce, true); - assert.equal(checkZipStub.calledWith(testTheme), true); - sinon.assert.notCalled(checkStub); - assert.equal(formatStub.calledOnce, false); - }); + await assert.rejects(() => ( + validate.check(testTheme.name, testTheme, {isZip: true}) + ), {message: 'invalid zip file'}); + + sinon.assert.calledOnce(checkZipStub); + sinon.assert.calledWith(checkZipStub, testTheme); + sinon.assert.notCalled(checkStub); + sinon.assert.notCalled(formatStub); }); }); diff --git a/ghost/core/test/utils/assertions.js b/ghost/core/test/utils/assertions.js index 1538e0f733d..9d768da3686 100644 --- a/ghost/core/test/utils/assertions.js +++ b/ghost/core/test/utils/assertions.js @@ -1,4 +1,6 @@ const assert = require('node:assert/strict'); +const {inspect, isDeepStrictEqual} = require('node:util'); +const {isPlainObject} = require('lodash'); const {snapshotManager} = require('@tryghost/express-test').snapshot; /** @@ -19,6 +21,22 @@ function assertMatchSnapshot(obj, properties) { assert(result.pass, result.message()); } +/** + * @template T + * @param {ReadonlyArray} arr + * @param {ReadonlyArray} expectedElements + * @param {string} [message] + * @returns {void} + */ +function assertArrayContainsDeep(arr, expectedElements, message) { + for (const expectedElement of expectedElements) { + assert( + arr.some(el => isDeepStrictEqual(el, expectedElement)), + message || `Expected ${inspect(expectedElement)} to be found` + ); + } +} + /** * @template T * @param {T} obj @@ -28,16 +46,21 @@ function assertMatchSnapshot(obj, properties) { */ function assertObjectMatches(obj, properties, message) { for (const [key, value] of Object.entries(properties)) { - assert.equal( - obj[key], - value, - message || `Property mismatch for key "${key}"` - ); + if (isPlainObject(obj[key])) { + assertObjectMatches(obj[key], value, message); + } else { + assert.deepEqual( + obj[key], + value, + message || `Property mismatch for key "${key}"` + ); + } } } module.exports = { assertExists, assertMatchSnapshot, + assertArrayContainsDeep, assertObjectMatches };