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
138 changes: 138 additions & 0 deletions e2e/downloads.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,84 @@ 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: the result three seconds, and what a
* view reads for its own colouring (a diagram's found entities and pathways,
* the overview's tree) six, so a page ready on the result alone is caught as
* well as one ready before it. 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';
const result = url.pathname.endsWith(`/token/${TOKEN}`);
if (resource === 'UNIPROT')
await new Promise((resolve) => setTimeout(resolve, result ? 3_000 : 6_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 +783,64 @@ 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, and finds nothing, so these say
// when the page is ready and what it says it carries, not what colour it is;
// the figures are recorded ones. The illustration colours itself as its
// result arrives, so its own wait is not one these can tell apart.
// Each view waits for the load its own colouring comes from: a page ready on
// the result alone was ready before the diagram even asked for its entities.
for (const [view, path, own] of [
['a diagram', `/PathwayBrowser/render/${DIAGRAM}`, '/found/all/'],
['an illustration', `/PathwayBrowser/render/${ILLUSTRATION}`, `/token/${TOKEN}`],
['the genome-wide view', '/PathwayBrowser/render?species=9606', '/eventsHierarchy/'],
] as const) {
test(`is not ready until ${view} has its analysis, 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' && load.url.includes(own)).length,
{ message: `the page read ${own} for UNIPROT`, timeout: 20_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');
});
});
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":"connection","value":"close"},{"name":"content-length","value":"2"},{"name":"content-type","value":"text/plain;charset=UTF-8"},{"name":"date","value":"Mon, 12 Oct 2026 03:31:22 GMT"}],"content":{"mimeType":"text/plain;charset=UTF-8","_file":"812ed4562d3211363a7b813aa9cd2cf042b63bb2.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":"connection","value":"close"},{"name":"content-length","value":"3237"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 12 Oct 2026 03:31:23 GMT"},{"name":"etag","value":"W/\"ca5-sa7k1xX74ycTbey3jmBLCrhGTO0\""}],"content":{"mimeType":"application/json; charset=utf-8","_file":"b1aee4d715fbe327136decb78e604b0ab8464ced.json"}}},{"request":{"method":"GET","url":"http://127.0.0.1:4253/ContentService/data/query/enhanced/v2/R-HSA-109606?fetchIncomingRelationships=true&summariseReferenceEntity=false&includeDisease=true&includeRef=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-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Thu, 17 Sep 2026 19:05:27 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"e6c5d5b52f592bbcc221640b529cd2e6cbdabee6.json"}}},{"request":{"method":"GET","url":"http://127.0.0.1:4253/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":"connection","value":"close"},{"name":"content-length","value":"1117"},{"name":"content-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Thu, 17 Sep 2026 19:05:28 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"7da0e1dc85b67aa8b981fb40bd44e860b499aee5.json"}}},{"request":{"method":"GET","url":"http://127.0.0.1:4253/ContentService/data/query/enhanced/v2/R-HSA-109606?fetchIncomingRelationships=true&summariseReferenceEntity=true&includeDisease=true&includeRef=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-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Thu, 17 Sep 2026 19:05:27 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"e6c5d5b52f592bbcc221640b529cd2e6cbdabee6.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":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, 12 Oct 2026 03:31:22 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"70588a05ec802f3b42d4de1136192d2ff069b195.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":"connection","value":"close"},{"name":"content-length","value":"2"},{"name":"content-type","value":"text/plain;charset=UTF-8"},{"name":"date","value":"Mon, 12 Oct 2026 03:31:22 GMT"}],"content":{"mimeType":"text/plain;charset=UTF-8","_file":"812ed4562d3211363a7b813aa9cd2cf042b63bb2.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":"connection","value":"close"},{"name":"content-length","value":"3237"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 12 Oct 2026 03:31:23 GMT"},{"name":"etag","value":"W/\"ca5-sa7k1xX74ycTbey3jmBLCrhGTO0\""}],"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":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, 12 Oct 2026 03:31:22 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"70588a05ec802f3b42d4de1136192d2ff069b195.json"}}},{"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, 12 Oct 2026 03:31:22 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"70588a05ec802f3b42d4de1136192d2ff069b195.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":"connection","value":"close"},{"name":"content-length","value":"2"},{"name":"content-type","value":"text/plain;charset=UTF-8"},{"name":"date","value":"Mon, 12 Oct 2026 03:31:22 GMT"}],"content":{"mimeType":"text/plain;charset=UTF-8","_file":"812ed4562d3211363a7b813aa9cd2cf042b63bb2.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":"connection","value":"close"},{"name":"content-length","value":"3237"},{"name":"content-type","value":"application/json; charset=utf-8"},{"name":"date","value":"Mon, 12 Oct 2026 03:31:23 GMT"},{"name":"etag","value":"W/\"ca5-sa7k1xX74ycTbey3jmBLCrhGTO0\""}],"content":{"mimeType":"application/json; charset=utf-8","_file":"b1aee4d715fbe327136decb78e604b0ab8464ced.json"}}},{"request":{"method":"GET","url":"http://127.0.0.1:4253/ContentService/data/query/enhanced/v2/R-HSA-109606?fetchIncomingRelationships=true&summariseReferenceEntity=false&includeDisease=true&includeRef=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-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Thu, 17 Sep 2026 19:05:27 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"e6c5d5b52f592bbcc221640b529cd2e6cbdabee6.json"}}},{"request":{"method":"GET","url":"http://127.0.0.1:4253/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":"connection","value":"close"},{"name":"content-length","value":"1117"},{"name":"content-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Thu, 17 Sep 2026 19:05:28 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"7da0e1dc85b67aa8b981fb40bd44e860b499aee5.json"}}},{"request":{"method":"GET","url":"http://127.0.0.1:4253/ContentService/data/query/enhanced/v2/R-HSA-109606?fetchIncomingRelationships=true&summariseReferenceEntity=true&includeDisease=true&includeRef=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-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Thu, 17 Sep 2026 19:05:27 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"e6c5d5b52f592bbcc221640b529cd2e6cbdabee6.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":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, 12 Oct 2026 03:31:22 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"70588a05ec802f3b42d4de1136192d2ff069b195.json"}}}]}}
Loading
Loading