comparison hg-web/e2e/app_e2e_test.js @ 225:70de0c80d093 hg-web

[hg-web] Add side-by-side changeset diffs
author MrJuneJune <me@mrjunejune.com>
date Sun, 02 Aug 2026 14:41:09 -0700
parents 3007ef5fc0ed
children c5129452493e
comparison
equal deleted inserted replaced
224:3007ef5fc0ed 225:70de0c80d093
71 path.join(root, 'README.md'), 71 path.join(root, 'README.md'),
72 '# Fixture Repository\n\n<script>window.__hgWebXss = true</script>\n', 72 '# Fixture Repository\n\n<script>window.__hgWebXss = true</script>\n',
73 ); 73 );
74 fs.writeFileSync(path.join(root, 'docs', 'README.md'), '# Documentation\n\nNested README.\n'); 74 fs.writeFileSync(path.join(root, 'docs', 'README.md'), '# Documentation\n\nNested README.\n');
75 fs.writeFileSync(path.join(root, 'src', 'main.c'), 'int main(void) { return 0; }\n'); 75 fs.writeFileSync(path.join(root, 'src', 'main.c'), 'int main(void) { return 0; }\n');
76 fs.writeFileSync(
77 path.join(root, 'BUILD'),
78 'cc_library(\n name = "fixture",\n srcs = ["src/main.c"],\n)\n',
79 );
76 fs.writeFileSync( 80 fs.writeFileSync(
77 path.join(root, 'pixel.png'), 81 path.join(root, 'pixel.png'),
78 Buffer.from( 82 Buffer.from(
79 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wl2nWQAAAAASUVORK5CYII=', 83 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wl2nWQAAAAASUVORK5CYII=',
80 'base64', 84 'base64',
86 fs.writeFileSync(path.join(root, 'archive.bin'), Buffer.from([0, 1, 2, 3])); 90 fs.writeFileSync(path.join(root, 'archive.bin'), Buffer.from([0, 1, 2, 3]));
87 run('hg', ['--repository', root, 'add'], options); 91 run('hg', ['--repository', root, 'add'], options);
88 run('hg', ['--repository', root, 'commit', '-u', 'Test User <[email protected]>', '-m', 'Initial fixture'], options); 92 run('hg', ['--repository', root, 'commit', '-u', 'Test User <[email protected]>', '-m', 'Initial fixture'], options);
89 const firstNode = run('hg', ['--repository', root, 'log', '-r', '.', '-T', '{node}'], options); 93 const firstNode = run('hg', ['--repository', root, 'log', '-r', '.', '-T', '{node}'], options);
90 94
91 fs.appendFileSync(path.join(root, 'README.md'), '\nSecond revision.\n'); 95 fs.writeFileSync(
96 path.join(root, 'README.md'),
97 '# Updated Fixture Repository\n\n<script>window.__hgWebXss = true</script>\n\nSecond revision.\n',
98 );
92 fs.writeFileSync(path.join(root, 'new-file.txt'), 'new file\n'); 99 fs.writeFileSync(path.join(root, 'new-file.txt'), 'new file\n');
93 run('hg', ['add', 'new-file.txt'], { ...options, cwd: root }); 100 run('hg', ['add', 'new-file.txt'], { ...options, cwd: root });
94 run('hg', ['--repository', root, 'commit', '-u', 'Test User <[email protected]>', '-m', 'Update fixture'], options); 101 run('hg', ['--repository', root, 'commit', '-u', 'Test User <[email protected]>', '-m', 'Update fixture'], options);
95 const tipNode = run('hg', ['--repository', root, 'log', '-r', '.', '-T', '{node}'], options); 102 const tipNode = run('hg', ['--repository', root, 'log', '-r', '.', '-T', '{node}'], options);
96 return { firstNode, tipNode }; 103 return { firstNode, tipNode };
280 }); 287 });
281 288
282 await assertPageHasNoBrowserErrors(browser, '/directory', async page => { 289 await assertPageHasNoBrowserErrors(browser, '/directory', async page => {
283 await page.getByText('Repository Files').waitFor(); 290 await page.getByText('Repository Files').waitFor();
284 await page.getByRole('link', { name: 'README.md' }).first().click(); 291 await page.getByRole('link', { name: 'README.md' }).first().click();
285 await page.getByText('Fixture Repository').waitFor(); 292 await page.getByText('Updated Fixture Repository').waitFor();
286 assert.equal(await page.evaluate(() => window.__hgWebXss), undefined); 293 assert.equal(await page.evaluate(() => window.__hgWebXss), undefined);
287 await page.keyboard.press('Escape'); 294 await page.keyboard.press('Escape');
288 295
289 let delayedReadmeRequested = false; 296 let delayedReadmeRequested = false;
290 let markReadmeRequested; 297 let markReadmeRequested;
303 new Promise((_, reject) => { 310 new Promise((_, reject) => {
304 setTimeout(() => reject(new Error('Timed out waiting for delayed README request')), 5000); 311 setTimeout(() => reject(new Error('Timed out waiting for delayed README request')), 5000);
305 }), 312 }),
306 ]); 313 ]);
307 await page.getByRole('link', { name: 'root' }).click(); 314 await page.getByRole('link', { name: 'root' }).click();
308 await page.getByText('Fixture Repository').waitFor(); 315 await page.getByText('Updated Fixture Repository').waitFor();
309 await page.waitForTimeout(600); 316 await page.waitForTimeout(600);
310 assert.equal(await page.getByText('Documentation').count(), 0); 317 assert.equal(await page.getByText('Documentation').count(), 0);
311 assert.equal(delayedReadmeRequested, true); 318 assert.equal(delayedReadmeRequested, true);
312 319
313 await page.getByRole('link', { name: 'pixel.png' }).click(); 320 await page.getByRole('link', { name: 'pixel.png' }).click();
323 }); 330 });
324 assert.equal(await image.evaluate(element => element.naturalWidth), 1); 331 assert.equal(await image.evaluate(element => element.naturalWidth), 1);
325 await page.getByRole('dialog', { name: 'Preview pixel.png' }).waitFor(); 332 await page.getByRole('dialog', { name: 'Preview pixel.png' }).waitFor();
326 await page.keyboard.press('Escape'); 333 await page.keyboard.press('Escape');
327 334
335 await page.getByRole('link', { name: 'BUILD' }).click();
336 const buildCode = page.locator('code.language-python');
337 await buildCode.waitFor();
338 await page.getByText('cc_library').waitFor();
339 assert.match(await buildCode.textContent(), /name = "fixture"/);
340 await page.keyboard.press('Escape');
341
328 await page.getByRole('link', { name: 'src' }).click(); 342 await page.getByRole('link', { name: 'src' }).click();
329 await page.getByRole('link', { name: 'main.c' }).click(); 343 await page.getByRole('link', { name: 'main.c' }).click();
330 await page.getByText('int main(void)').waitFor(); 344 await page.getByText('int main(void)').waitFor();
331 await page.keyboard.press('Escape'); 345 await page.keyboard.press('Escape');
332 }); 346 });
353 await assertPageHasNoBrowserErrors(browser, `/changeset/${tipNode}`, async page => { 367 await assertPageHasNoBrowserErrors(browser, `/changeset/${tipNode}`, async page => {
354 await page.getByRole('heading', { name: 'Update fixture' }).waitFor(); 368 await page.getByRole('heading', { name: 'Update fixture' }).waitFor();
355 await page.locator('.changeset-files code').filter({ hasText: 'new-file.txt' }).waitFor(); 369 await page.locator('.changeset-files code').filter({ hasText: 'new-file.txt' }).waitFor();
356 await page.getByText('added', { exact: true }).waitFor(); 370 await page.getByText('added', { exact: true }).waitFor();
357 await page.getByRole('region', { name: 'Changeset diff' }).waitFor(); 371 await page.getByRole('region', { name: 'Changeset diff' }).waitFor();
372 await page.locator('.diff-column-headings').getByText('Before').first().waitFor();
373 await page.locator('.diff-column-headings').getByText('After').first().waitFor();
374 await page.locator('.diff-left.diff-remove').filter({ hasText: '# Fixture Repository' }).waitFor();
375 await page.locator('.diff-right.diff-add').filter({ hasText: '# Updated Fixture Repository' }).waitFor();
376 assert.equal(
377 await page.locator('.diff-left.diff-context').filter({ hasText: '<script>' }).first().textContent(),
378 '<script>window.__hgWebXss = true</script>',
379 );
358 assert.equal( 380 assert.equal(
359 await page.locator('.changeset-paper').evaluate( 381 await page.locator('.changeset-paper').evaluate(
360 element => getComputedStyle(element).backgroundImage, 382 element => getComputedStyle(element).backgroundImage,
361 ), 383 ),
362 'none', 384 'none',