Skip to content
Closed
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
125 changes: 125 additions & 0 deletions e2e/downloads.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,79 @@ test.describe('Logo downloads', () => {
});
});

/** A made-up analysis token, answered by `holdAnalysisForUniprot`. */
const TOKEN = 'MADE-UP-RENDER-TOKEN';

/**
* Answer a made-up analysis holding one resource besides TOTAL, so the page
* loads it for TOTAL and then again for UNIPROT, as the browser shows it.
* Every load for UNIPROT is held back three seconds, and each load says
* whether the page was already ready when it arrived.
*/
async function holdAnalysisForUniprot(page: Page) {
const loads: { url: string; resource: string; readyBefore: boolean }[] = [];
const arrive = async (url: URL) => {
const resource = url.searchParams.get('resource') ?? 'TOTAL';
if (resource === 'UNIPROT') await new Promise((resolve) => setTimeout(resolve, 3_000));
const ready = await page
.locator('cr-render[data-render-ready="true"]')
.count()
.catch(() => 0);
loads.push({ url: url.pathname, resource, readyBefore: ready > 0 });
};
const result = {
summary: {
token: TOKEN,
type: 'OVERREPRESENTATION',
sampleName: 'made up',
text: true,
projection: false,
interactors: false,
includeDisease: true,
},
expression: { columnNames: [] },
identifiersNotFound: 0,
pathwaysFound: 0,
pathways: [],
resourceSummary: [
{ resource: 'TOTAL', pathways: 1, filtered: 1 },
{ resource: 'UNIPROT', pathways: 1, filtered: 1 },
],
speciesSummary: [],
};
await page.route(`**/AnalysisService/token/${TOKEN}**`, async (route) => {
const url = new URL(route.request().url());
if (url.pathname.endsWith('/notFound')) return route.fulfill({ json: [] });
await arrive(url);
if (url.pathname.endsWith(`/token/${TOKEN}`)) return route.fulfill({ json: result });
if (url.pathname.endsWith('/filter/pathways')) return route.fulfill({ json: [] });
if (url.pathname.includes('/found/all/')) {
return route.fulfill({
json: {
pathway: url.pathname.split('/').at(-1),
foundEntities: 0,
foundInteractors: 0,
expNames: [],
resources: [url.searchParams.get('resource') ?? 'TOTAL'],
entities: [],
interactors: [],
},
});
}
return route.fulfill({ status: 404, json: { code: 404 } });
});
// The overview's tree is fetched with the token, which the content service
// cannot read: it is answered with the recorded tree without one.
await page.route('**/ContentService/data/eventsHierarchy/**', async (route) => {
const url = new URL(route.request().url());
if (url.searchParams.get('token') !== TOKEN) return route.fallback();
await arrive(url);
for (const name of ['token', 'resource', 'interactors']) url.searchParams.delete(name);
return route.fallback({ url: url.toString() });
});
return loads;
}

test.describe('The render page, without the render service', () => {
// The slide's grouping (#299) was checked only inside the PPTX download
// test, which needs the render service and so never runs in CI. What makes
Expand Down Expand Up @@ -705,4 +778,56 @@ test.describe('The render page, without the render service', () => {
expect([...names], `group ${group} has one name`).toHaveLength(1);
}
});

// The page drew a figure and said it was ready before its analysis
// colouring arrived -- and, for a result holding one resource besides TOTAL,
// before the second load that colours it for that resource. The render
// service then cached the uncoloured figure under the token. The analysis
// here is made up and answered in the test; the figures are recorded ones.
for (const [view, path] of [
['a diagram', `/PathwayBrowser/render/${DIAGRAM}`],
['an illustration', `/PathwayBrowser/render/${ILLUSTRATION}`],
['the genome-wide view', '/PathwayBrowser/render?species=9606'],
] as const) {
test(`is not ready until ${view} is coloured, for the result’s resource`, async ({ page }) => {
const loads = await holdAnalysisForUniprot(page);
const at = `${path}${path.includes('?') ? '&' : '?'}analysis=${TOKEN}`;
await page.goto(at);
await expect(page.locator('cr-render[data-render-ready="true"]')).toHaveCount(1, {
timeout: 120_000,
});
const state = await page.evaluate(
() => (window as unknown as { __renderState: { analysis: unknown } }).__renderState
);
expect(state.analysis, 'coloured for the one resource besides TOTAL').toEqual({
token: TOKEN,
resource: 'UNIPROT',
});
// Waited for rather than read: a page ready too early would be read
// before the held-back loads even arrived.
await expect
.poll(() => loads.filter((load) => load.resource === 'UNIPROT').length, {
message: 'the page read the analysis for UNIPROT',
timeout: 15_000,
})
.toBeGreaterThan(0);
expect(
loads.filter((load) => load.readyBefore),
'no colouring arrived after the page said it was ready'
).toEqual([]);
});
}

// A token whose result cannot be read -- one a release has removed -- is
// dropped by the analysis service, and the page then drew the figure with no
// colouring at all and said it was ready.
test('fails, rather than draw uncoloured, when the analysis cannot be read', async ({ page }) => {
await page.route(`**/AnalysisService/token/${TOKEN}**`, (route) =>
route.fulfill({ status: 410, json: { code: 410, reason: 'Gone' } })
);
await page.goto(`/PathwayBrowser/render/${DIAGRAM}?analysis=${TOKEN}`);
const render = page.locator('cr-render');
await expect(render).toHaveAttribute('data-render-error', /analysis/, { timeout: 120_000 });
await expect(render).not.toHaveAttribute('data-render-ready', 'true');
});
});
1 change: 1 addition & 0 deletions e2e/har/14dd5e5fc69120442880bca51a386f483b355ff1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[{"dbId":109581,"displayName":"Apoptosis","stId":"R-HSA-109581","name":["Apoptosis"],"stIdVersion":"R-HSA-109581.6","oldStId":"REACT_578","schemaClass":"Pathway","speciesName":"Homo sapiens","releaseDate":"2004-09-20","hasDiagram":true,"hasEHLD":true,"diagramWidth":770,"diagramHeight":737,"inferred":false,"inDisease":false},{"dbId":5357801,"displayName":"Programmed Cell Death","stId":"R-HSA-5357801","name":["Programmed Cell Death"],"stIdVersion":"R-HSA-5357801.4","oldStId":"REACT_268456","schemaClass":"TopLevelPathway","speciesName":"Homo sapiens","releaseDate":"2014-12-11","hasDiagram":true,"hasEHLD":true,"diagramWidth":548,"diagramHeight":259,"inferred":false,"inDisease":false}]]
1 change: 1 addition & 0 deletions e2e/har/31bd9b9f5f7b338e41b56183a2f3008b541d7c84.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
98
1 change: 1 addition & 0 deletions e2e/har/3ec1e7528ba1789ebf0efd89d4e0cf0dc596d362.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions e2e/har/904ce1cf1cf6ad8a0c4158caa05be270fb305ae7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[{"dbId":109606,"displayName":"Intrinsic Pathway for Apoptosis","stId":"R-HSA-109606","name":["Intrinsic Pathway for Apoptosis"],"stIdVersion":"R-HSA-109606.5","oldStId":"REACT_964","schemaClass":"Pathway","doi":"10.3180/REACT_964.1","speciesName":"Homo sapiens","releaseDate":"2004-10-27","hasDiagram":true,"hasEHLD":false,"diagramWidth":3107,"diagramHeight":1718,"inferred":false,"inDisease":false},{"dbId":109581,"displayName":"Apoptosis","stId":"R-HSA-109581","name":["Apoptosis"],"stIdVersion":"R-HSA-109581.6","oldStId":"REACT_578","schemaClass":"Pathway","speciesName":"Homo sapiens","releaseDate":"2004-09-20","hasDiagram":true,"hasEHLD":true,"diagramWidth":770,"diagramHeight":737,"inferred":false,"inDisease":false},{"dbId":5357801,"displayName":"Programmed Cell Death","stId":"R-HSA-5357801","name":["Programmed Cell Death"],"stIdVersion":"R-HSA-5357801.4","oldStId":"REACT_268456","schemaClass":"TopLevelPathway","speciesName":"Homo sapiens","releaseDate":"2014-12-11","hasDiagram":true,"hasEHLD":true,"diagramWidth":548,"diagramHeight":259,"inferred":false,"inDisease":false}]]
1 change: 1 addition & 0 deletions e2e/har/aad81e2c311411a9a939b1ecc189621e16aa3bb9.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions e2e/har/d0997186d989c4275175d1fe35ab050d5f82c22d.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"log":{"version":"1.2","entries":[{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/database/version"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"cache-control","value":"public, max-age=3600, s-maxage=31536000"},{"name":"connection","value":"close"},{"name":"content-length","value":"2"},{"name":"content-type","value":"text/plain; charset=utf-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:39:30 GMT"},{"name":"etag","value":"W/\"2-Mb2bn197M45BtWGDovMAi1QdfIQ\""},{"name":"x-cache-rule","value":"standard"}],"content":{"mimeType":"text/plain; charset=utf-8","_file":"31bd9b9f5f7b338e41b56183a2f3008b541d7c84.txt"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/species/main"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"cache-control","value":"public, max-age=3600, s-maxage=31536000"},{"name":"connection","value":"close"},{"name":"content-length","value":"3237"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:39:30 GMT"},{"name":"etag","value":"W/\"ca5-sa7k1xX74ycTbey3jmBLCrhGTO0\""},{"name":"x-cache-rule","value":"standard"}],"content":{"mimeType":"application/json; charset=utf-8","_file":"b1aee4d715fbe327136decb78e604b0ab8464ced.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/query/enhanced/v2/R-HSA-109606?fetchIncomingRelationships=true&summariseReferenceEntity=false&includeDisease=true&includeRef=true&view=nested-aggregated"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"cache-control","value":"public, max-age=3600, s-maxage=31536000"},{"name":"connection","value":"close"},{"name":"content-length","value":"107409"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:39:31 GMT"},{"name":"etag","value":"W/\"1a391-0JlxhtmJxCdRddH+NasFDV+Cwi0\""},{"name":"x-cache-rule","value":"standard"}],"content":{"mimeType":"application/json; charset=utf-8","_file":"d0997186d989c4275175d1fe35ab050d5f82c22d.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/event/R-HSA-109606/ancestors?includeRef=true&view=nested-aggregated"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"cache-control","value":"public, max-age=3600, s-maxage=31536000"},{"name":"connection","value":"close"},{"name":"content-length","value":"1091"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:39:30 GMT"},{"name":"etag","value":"W/\"443-kEzhzxz2rYoMQVjKoFvicPswWuc\""},{"name":"x-cache-rule","value":"standard"}],"content":{"mimeType":"application/json; charset=utf-8","_file":"904ce1cf1cf6ad8a0c4158caa05be270fb305ae7.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/query/enhanced/v2/R-HSA-109606?fetchIncomingRelationships=true&summariseReferenceEntity=true&includeDisease=true&includeRef=true&view=nested-aggregated"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"cache-control","value":"public, max-age=3600, s-maxage=31536000"},{"name":"connection","value":"close"},{"name":"content-length","value":"107409"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:39:30 GMT"},{"name":"etag","value":"W/\"1a391-0JlxhtmJxCdRddH+NasFDV+Cwi0\""},{"name":"x-cache-rule","value":"standard"}],"content":{"mimeType":"application/json; charset=utf-8","_file":"d0997186d989c4275175d1fe35ab050d5f82c22d.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/eventsHierarchy/9606?pathwaysOnly=true&token=MADE-UP-RENDER-TOKEN&view=nested-aggregated"},"response":{"status":-1,"headers":[],"content":{"mimeType":"x-unknown"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/eventsHierarchy/9606?pathwaysOnly=true&view=nested-aggregated"},"response":{"status":-1,"headers":[],"content":{"mimeType":"x-unknown"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/eventsHierarchy/9606?pathwaysOnly=true&view=nested-aggregated"},"response":{"status":200,"headers":[{"name":"Transfer-Encoding","value":"chunked"},{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"connection","value":"close"},{"name":"content-disposition","value":"inline; swaggerDownload=\"attachment\"; filename=\"9606.json\""},{"name":"content-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:39:30 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"3ec1e7528ba1789ebf0efd89d4e0cf0dc596d362.json"}}}]}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"log":{"version":"1.2","entries":[{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/database/version"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"cache-control","value":"public, max-age=3600, s-maxage=31536000"},{"name":"connection","value":"close"},{"name":"content-length","value":"2"},{"name":"content-type","value":"text/plain; charset=utf-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:36:14 GMT"},{"name":"etag","value":"W/\"2-Mb2bn197M45BtWGDovMAi1QdfIQ\""},{"name":"x-cache-rule","value":"standard"}],"content":{"mimeType":"text/plain; charset=utf-8","_file":"31bd9b9f5f7b338e41b56183a2f3008b541d7c84.txt"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/species/main"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"cache-control","value":"public, max-age=3600, s-maxage=31536000"},{"name":"connection","value":"close"},{"name":"content-length","value":"3237"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:36:14 GMT"},{"name":"etag","value":"W/\"ca5-sa7k1xX74ycTbey3jmBLCrhGTO0\""},{"name":"x-cache-rule","value":"standard"}],"content":{"mimeType":"application/json; charset=utf-8","_file":"b1aee4d715fbe327136decb78e604b0ab8464ced.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/eventsHierarchy/9606?pathwaysOnly=true&view=nested-aggregated"},"response":{"status":-1,"headers":[],"content":{"mimeType":"x-unknown"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/eventsHierarchy/9606?pathwaysOnly=true&view=nested-aggregated"},"response":{"status":-1,"headers":[],"content":{"mimeType":"x-unknown"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/eventsHierarchy/9606?pathwaysOnly=true&view=nested-aggregated"},"response":{"status":200,"headers":[{"name":"Transfer-Encoding","value":"chunked"},{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"connection","value":"close"},{"name":"content-disposition","value":"inline; swaggerDownload=\"attachment\"; filename=\"9606.json\""},{"name":"content-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Mon, 02 Nov 2026 02:36:20 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"3ec1e7528ba1789ebf0efd89d4e0cf0dc596d362.json"}}}]}}
Loading
Loading