diff --git a/ui-tests/test/mobile.spec.ts b/ui-tests/test/mobile.spec.ts index d7bdaff92..3428b3aa2 100644 --- a/ui-tests/test/mobile.spec.ts +++ b/ui-tests/test/mobile.spec.ts @@ -6,6 +6,7 @@ import path from 'path'; import { expect } from '@playwright/test'; import { test } from './fixtures'; +import { waitForKernelReady } from './utils'; test.use({ autoGoto: false, viewport: { width: 512, height: 768 } }); @@ -33,21 +34,7 @@ test.describe('Mobile', () => { // await page.notebook.run(); // wait for the kernel status animations to be finished - await page.waitForSelector('.jp-RetroKernelStatus-fade'); - await page.waitForFunction(() => { - const status = window.document.getElementsByClassName( - 'jp-RetroKernelStatus' - )[0]; - - if (!status) { - return false; - } - - const finished = status?.getAnimations().reduce((prev, curr) => { - return prev && curr.playState === 'finished'; - }, true); - return finished; - }); + await waitForKernelReady(page); expect(await page.screenshot()).toMatchSnapshot('notebook.png'); }); diff --git a/ui-tests/test/notebook.spec.ts b/ui-tests/test/notebook.spec.ts index f99e4edc2..a9248d311 100644 --- a/ui-tests/test/notebook.spec.ts +++ b/ui-tests/test/notebook.spec.ts @@ -3,9 +3,11 @@ import path from 'path'; +import { expect } from '@playwright/test'; + import { test } from './fixtures'; -import { expect } from '@playwright/test'; +import { runAndAdvance, waitForKernelReady } from './utils'; const NOTEBOOK = 'example.ipynb'; @@ -55,4 +57,38 @@ test.describe('Notebook', () => { const url = page.url(); expect(url).toContain(newNameStripped); }); + + // TODO: rewrite with page.notebook when fixed upstream in Galata + // and usable in RetroLab without active tabs + test('Outputs should be scrolled automatically', async ({ + page, + tmpPath + }) => { + const notebook = 'autoscroll.ipynb'; + await page.contents.uploadFile( + path.resolve(__dirname, `./notebooks/${notebook}`), + `${tmpPath}/${notebook}` + ); + await page.goto(`notebooks/${tmpPath}/${notebook}`); + + await waitForKernelReady(page); + // run the two cells + await runAndAdvance(page); + await runAndAdvance(page); + + await page.waitForSelector('.jp-Cell-outputArea pre'); + + const checkCell = async (n: number): Promise => { + const scrolled = await page.$eval(`.jp-Notebook-cell >> nth=${n}`, el => + el.classList.contains('jp-mod-outputsScrolled') + ); + return scrolled; + }; + + // check the long output area is auto scrolled + expect(await checkCell(0)).toBe(true); + + // check the short output area is not auto scrolled + expect(await checkCell(1)).toBe(false); + }); }); diff --git a/ui-tests/test/notebooks/autoscroll.ipynb b/ui-tests/test/notebooks/autoscroll.ipynb new file mode 100644 index 000000000..d386426b1 --- /dev/null +++ b/ui-tests/test/notebooks/autoscroll.ipynb @@ -0,0 +1,41 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "6f7028b9-4d2c-4fa2-96ee-bfa77bbee434", + "metadata": {}, + "outputs": [], + "source": ["print('1\\n' * 200)"] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f7028b9-4d2c-4fa2-96ee-bfa77bbee434", + "metadata": {}, + "outputs": [], + "source": ["print('1\\n' * 20)"] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ui-tests/test/utils.ts b/ui-tests/test/utils.ts new file mode 100644 index 000000000..1e71ac4ea --- /dev/null +++ b/ui-tests/test/utils.ts @@ -0,0 +1,35 @@ +import { IJupyterLabPageFixture } from '@jupyterlab/galata'; + +/** + * Run the selected cell and advance. + */ +export async function runAndAdvance( + page: IJupyterLabPageFixture +): Promise { + await page.click( + "//button[normalize-space(@title)='Run the selected cells and advance']" + ); +} + +/** + * Wait for the kernel to be ready + */ +export async function waitForKernelReady( + page: IJupyterLabPageFixture +): Promise { + await page.waitForSelector('.jp-RetroKernelStatus-fade'); + await page.waitForFunction(() => { + const status = window.document.getElementsByClassName( + 'jp-RetroKernelStatus' + )[0]; + + if (!status) { + return false; + } + + const finished = status?.getAnimations().reduce((prev, curr) => { + return prev && curr.playState === 'finished'; + }, true); + return finished; + }); +}