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
2 changes: 2 additions & 0 deletions docs/live-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ Unit tests verify configuration rejection, URL separation, report-readiness pred
The outcome JSON contains an explicit `skipped` entry for missing-image-alternative result validation. Passing smoke checks establish authentication, report lookup, fresh draft handoff and loading-state exit; they do not establish correct prepublish results. This follows the [WordPress live runner](https://github.com/Siteimprove/CMS-plugin-Wordpress/blob/master/tests/live/run.js).

After login, the test waits for authenticated report data for the mapped URL before opening the report panel, matching the WordPress flow. Diagnostic fields are independently filtered by the reporter; they contain no raw response fields, account text or URLs.

Report identity is checked against the exact `url` parameter on the SDK polling request. Like WordPress, the response must be authenticated, error-free, include a nonnegative numeric issue count and a nonempty `mainUrl`; that response field is not assumed to equal the crawled page URL.
2 changes: 1 addition & 1 deletion tests/live/diagnostics.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const flags = ['entitlementReady', 'pollSeen', 'pollOk', 'pollAuthenticated', 'pollUrlMatches',
'pollIssueCountValid', 'pollErrorNone', 'panelFramePresent', 'panelVisible', 'launcherVisible',
'pollMainUrlPresent', 'pollIssueCountValid', 'pollErrorNone', 'panelFramePresent', 'panelVisible', 'launcherVisible',
'blockedExternalRequest'];

// Account text and arbitrary response fields must never enter public artifacts.
Expand Down
5 changes: 3 additions & 2 deletions tests/live/editor.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ export async function openLiveEditor(page, context) {
diagnostic.pollOk = response.ok();
const body = await response.json();
diagnostic.pollAuthenticated = body?.authed === true;
diagnostic.pollUrlMatches = url.searchParams.get('url') === config.crawledUrl && body?.mainUrl === config.crawledUrl;
diagnostic.pollUrlMatches = url.searchParams.get('url') === config.crawledUrl;
diagnostic.pollMainUrlPresent = typeof body?.mainUrl === 'string' && body.mainUrl.trim().length > 0;
diagnostic.pollIssueCountValid = Number.isFinite(body?.issues) && body.issues >= 0;
diagnostic.pollErrorNone = body?.error === 'None';
if (diagnostic.pollUrlMatches && response.ok() && isReport(body, config.crawledUrl)) reportReceived = true;
if (diagnostic.pollUrlMatches && response.ok() && isReport(body, url.searchParams.get('url'), config.crawledUrl)) reportReceived = true;
}
} catch { /* Invalid or failed responses cannot satisfy report readiness. */ }
});
Expand Down
5 changes: 3 additions & 2 deletions tests/live/settings.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export function allowedRequest(value) {
&& ['siteimprove.com', 'siteimprove.net'].some(host => url.hostname === host || url.hostname.endsWith('.' + host)));
}

export function isReport(value, expectedUrl) {
export function isReport(value, requestedUrl, expectedUrl) {
return value?.authed === true && value.error === 'None' && Number.isFinite(value.issues)
&& value.issues >= 0 && value.mainUrl === expectedUrl;
&& value.issues >= 0 && requestedUrl === expectedUrl
&& typeof value.mainUrl === 'string' && value.mainUrl.trim().length > 0;
}
12 changes: 7 additions & 5 deletions tests/unit/live.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ test('missing credentials and unsupported or mismatched public paths fail closed
}
});

test('only authenticated successful data for the expected page proves report readiness', async () => {
test('report readiness matches the requested page without treating mainUrl as its identity', async () => {
const { isReport } = await import('../live/settings.mjs');
const expected = fixture().SITEIMPROVE_CRAWLED_URL;
const body = { authed: true, error: 'None', issues: 0, mainUrl: expected };
assert.equal(isReport(body, expected), true);
const body = { authed: true, error: 'None', issues: 0, mainUrl: 'https://my2.siteimprove.com/report' };
assert.equal(isReport(body, expected, expected), true);
for (const patch of [{ authed: false }, { error: 'Failed' }, { issues: -1 },
{ issues: '0' }, { mainUrl: 'https://other.example.test/' }]) {
assert.equal(isReport({ ...body, ...patch }, expected), false);
{ issues: '0' }, { mainUrl: '' }, { mainUrl: null }, { mainUrl: ' ' }]) {
assert.equal(isReport({ ...body, ...patch }, expected, expected), false);
}
for (const requested of [null, 'https://other.example.test/', expected + '?preview=1'])
assert.equal(isReport(body, requested, expected), false);
});

test('browser requests reject lookalike login domains and unrelated hosts', async () => {
Expand Down
Loading