Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/admin-x-framework/src/test/vitest-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export function createVitestConfig(options: VitestConfigOptions = {}) {
test: {
globals: true,
environment: 'jsdom',
// pool: 'forks' for process-level isolation in jsdom-heavy
// React suites; sidesteps Vitest threads-pool edge cases.
pool: 'forks',
isolate: true,
setupFiles,
include,
silent,
Expand Down
20 changes: 15 additions & 5 deletions ghost/core/test/e2e-api/members/send-magic-link.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -768,14 +768,24 @@ describe('sendMagicLink', function () {
function assertNoOTCInEmailContent(mail) {
const otcRegex = /\d{6}|\scode\s|\sotc\s/i;

// \d{6} would otherwise match digits inside the magic-link token.
const stripUrls = string => string.replace(/https?:\/\/\S+/g, ' ');

const subjectMatch = mail.subject.match(otcRegex);
assert(!subjectMatch, `Email subject should not contain OTC. Found: "${subjectMatch?.[0]}" in subject: "${mail.subject}"`);

const textMatch = mail.text.match(otcRegex);
assert(!textMatch, `Email text should not contain OTC. Found: "${textMatch?.[0]}" near: "${mail.text.substring(mail.text.search(otcRegex) - 50, mail.text.search(otcRegex) + 100)}"`);

// It's possible that there's an OTC-like in an href, so only check the rendered text.
const htmlText = cheerio.load(mail.html).text();
const text = stripUrls(mail.text);
const textMatch = text.match(otcRegex);
assert(!textMatch, `Email text should not contain OTC. Found: "${textMatch?.[0]}" near: "${text.substring(text.search(otcRegex) - 50, text.search(otcRegex) + 100)}"`);

// Per text node — cheerio's .text() concatenates without
// separators, so URLs could otherwise merge with adjacent digits.
const $ = cheerio.load(mail.html);
const htmlText = $('*').contents()
.toArray()
.filter(n => n.type === 'text')
.map(n => stripUrls(n.data))
.join(' ');
const htmlMatch = htmlText.match(otcRegex);
assert(!htmlMatch, `Email HTML should not contain OTC. Found: "${htmlMatch?.[0]}" near: "${htmlText.substring(htmlText.search(otcRegex) - 50, htmlText.search(otcRegex) + 100)}"`);
}
Expand Down
40 changes: 23 additions & 17 deletions ghost/core/test/unit/frontend/helpers/get.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -551,23 +551,29 @@ describe('{{#get}} helper', function () {
});

it('should log an error and return safely if it hits the timeout threshold', async function () {
configUtils.set('optimization:getHelper:timeout:threshold', 1);

const result = await get.call(
{},
'posts',
{hash: {}, data: locals, fn: fn, inverse: inverse}
);

assert(result.toString().includes('data-aborted-get-helper'));
// A log message will be output
sinon.assert.calledOnce(logging.error);
// The get helper gets called with an empty array of results
sinon.assert.calledOnce(fn);
const args = fn.firstCall.args[0];
assert(args && typeof args === 'object');
assert('posts' in args);
assert.deepEqual(args.posts, []);
const clock = sinon.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
try {
configUtils.set('optimization:getHelper:timeout:threshold', 1);

const resultPromise = get.call(
{},
'posts',
{hash: {}, data: locals, fn: fn, inverse: inverse}
);
// 2 > threshold (1), < stub's 5 — fires only the helper's timer.
await clock.tickAsync(2);
const result = await resultPromise;

assert(result.toString().includes('data-aborted-get-helper'));
sinon.assert.calledOnce(logging.error);
sinon.assert.calledOnce(fn);
const args = fn.firstCall.args[0];
assert(args && typeof args === 'object');
assert('posts' in args);
assert.deepEqual(args.posts, []);
} finally {
clock.restore();
}
});
});

Expand Down
16 changes: 15 additions & 1 deletion ghost/core/test/unit/frontend/helpers/recommendations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ describe('{{#recommendations}} helper', function () {
});

describe('when timeout is exceeded', function () {
let clock;

before(function () {
sinon.stub(api, 'recommendationsPublic').get(() => {
return {
Expand All @@ -158,16 +160,28 @@ describe('{{#recommendations}} helper', function () {
};
});
});

beforeEach(function () {
clock = sinon.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
});

afterEach(function () {
clock.restore();
});

after(async function () {
await configUtils.restore();
});

it('should log an error and return safely if it hits the timeout threshold', async function () {
configUtils.set('optimization:getHelper:timeout:threshold', 1);

const response = await recommendations.call(
const responsePromise = recommendations.call(
'recommendations'
);
// 2 > threshold (1), < stub's 5 — fires only the helper's timer.
await clock.tickAsync(2);
const response = await responsePromise;

// An error message is logged
sinon.assert.calledOnce(logging.error);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"form-data@<2.5.4": "^2.5.4",
"growl@<1.10.0": "^1.10.0",
"handlebars@>=4.0.0 <=4.7.8": "^4.7.9",
"ip-address@<=10.1.0": "^10.2.0",
"js-yaml@>=4.0.0 <4.1.1": "^4.1.1",
"json5@<1.0.2": "^1.0.2",
"lodash@<4.18.0": "^4.18.0",
Expand Down
9 changes: 5 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading