diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 17ad2201..4b9ee461 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,8 @@ jobs: - name: Lint the extension run: | set -eux - jlpm + jlpm install + jlpm run lint:check - name: Test the extension run: | @@ -94,59 +95,3 @@ jobs: jupyter labextension list jupyter labextension list 2>&1 | grep -ie "@jupyterlab/scheduler.*OK" python -m jupyterlab.browser_check --no-chrome-test - - integration-tests: - name: Integration tests - needs: build - runs-on: ubuntu-latest - - env: - PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/pw-browsers - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Base Setup - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - - - name: Download extension package - uses: actions/download-artifact@v2 - with: - name: extension-artifacts - - - name: Install the extension - run: | - set -eux - python -m pip install "jupyterlab~=3.1" jupyter_scheduler*.whl - - - name: Install dependencies - working-directory: ui-tests - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - run: jlpm install - - - name: Set up browser cache - uses: actions/cache@v2 - with: - path: | - ${{ github.workspace }}/pw-browsers - key: ${{ runner.os }}-${{ hashFiles('ui-tests/yarn.lock') }} - - - name: Install browser - run: jlpm playwright install chromium - working-directory: ui-tests - - - name: Execute integration tests - working-directory: ui-tests - run: | - jlpm playwright test - - - name: Upload Playwright Test report - if: always() - uses: actions/upload-artifact@v2 - with: - name: jupyter_scheduler-playwright-tests - path: | - ui-tests/test-results - ui-tests/playwright-report diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 00000000..9a7f7626 --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,71 @@ +name: E2E Tests + +# suppress warning raised by https://github.com/jupyter/jupyter_core/pull/292 +env: + JUPYTER_PLATFORM_DIRS: '1' + +on: + push: + branches: main + pull_request: + branches: '*' + +jobs: + e2e-tests: + name: Linux + runs-on: ubuntu-latest + + env: + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/pw-browsers + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Base Setup + uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 + + - name: Install extension dependencies + run: python -m pip install -U jupyterlab~=3.1 + + - name: Build the extension + run: | + set -eux + python -m pip install . + + jupyter server extension list + jupyter server extension list 2>&1 | grep -ie "jupyter_scheduler.*OK" + + jupyter labextension list + jupyter labextension list 2>&1 | grep -ie "@jupyterlab/scheduler.*OK" + python -m jupyterlab.browser_check + + - name: Install ui-tests dependencies + working-directory: ui-tests + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + run: jlpm install + + - name: Set up browser cache + uses: actions/cache@v2 + with: + path: | + ${{ github.workspace }}/pw-browsers + key: ${{ runner.os }}-${{ hashFiles('ui-tests/yarn.lock') }} + + - name: Install browser + working-directory: ui-tests + run: jlpm playwright install chromium + + - name: Execute integration tests + working-directory: ui-tests + run: jlpm test + + - name: Upload Playwright Test report + if: always() + uses: actions/upload-artifact@v2 + with: + name: jupyter_scheduler-playwright-tests-linux + path: | + ui-tests/test-results + ui-tests/playwright-report diff --git a/.github/workflows/update-integration-tests.yml b/.github/workflows/update-integration-tests.yml index 63241af2..3cc16fd1 100644 --- a/.github/workflows/update-integration-tests.yml +++ b/.github/workflows/update-integration-tests.yml @@ -2,6 +2,10 @@ name: Update Playwright Snapshots on: workflow_dispatch: + inputs: + number: + description: 'PR number' + required: true issue_comment: types: [created, edited] @@ -10,10 +14,8 @@ permissions: pull-requests: write jobs: - - update-snapshots: - if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, 'please update playwright snapshots') }} + if: ${{ github.event.inputs || (github.event.issue.pull_request && contains(github.event.comment.body, 'please update playwright snapshots')) }} runs-on: ubuntu-latest steps: @@ -26,7 +28,7 @@ jobs: run: git config --global hub.protocol https - name: Checkout the branch from the PR that triggered the job - run: hub pr checkout ${{ github.event.issue.number }} + run: hub pr checkout ${{ github.event.inputs.number || github.event.issue.number }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -36,7 +38,7 @@ jobs: python_version: '3.11' - name: Install dependencies - run: python -m pip install -U jupyterlab~=3.1 jupyter-archive + run: python -m pip install -U jupyterlab~=3.1 - name: Install extension run: | @@ -50,3 +52,5 @@ jobs: # Playwright knows how to start JupyterLab server start_server_script: 'null' test_folder: ui-tests + # use jlpm (not default yet) + npm_client: jlpm diff --git a/.gitignore b/.gitignore index 65455f93..c152fb44 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,10 @@ dev/jobs # jupyter releaser local checkout .jupyter_releaser_checkout + +# Yarn 3 cache +.yarn + +# Jest coverage reports and a side effect +coverage +junit.xml diff --git a/ui-tests/helpers/SchedulerHelper.ts b/ui-tests/helpers/SchedulerHelper.ts index c0fb1e53..55f53e95 100644 --- a/ui-tests/helpers/SchedulerHelper.ts +++ b/ui-tests/helpers/SchedulerHelper.ts @@ -1,64 +1,239 @@ -import { Page } from '@playwright/test'; +import { expect, IJupyterLabPageFixture } from '@jupyterlab/galata'; +import type { Locator, TestInfo } from '@playwright/test'; + +enum SELECTORS { + // tbutton = toolbar button + CREATE_JOB_TBUTTON = 'button.jp-ToolbarButtonComponent[data-command="scheduling:create-from-notebook"][title="Create a notebook job"]', + LAUNCHER_CARD = 'div.jp-LauncherCard[title="Notebook Jobs"]', + LIST_VIEW_TIMES = 'td.MuiTableCell-body:has-text(" AM"), td.MuiTableCell-body:has-text(" PM")', + NOTEBOOK_TOOLBAR = '.jp-NotebookPanel-toolbar[aria-label="notebook actions"]', + ENABLE_DEBUGGER_TBUTTON = '.jp-DebuggerBugButton', + KERNEL_NAME_TBUTTON = '.jp-KernelName', + EXECUTION_INDICATOR_TBUTTON = '.jp-Notebook-ExecutionIndicator' +} + +type SnapshotOptions = { + /** + * Crops the screenshot to a locator. Uses the current main area widget + * locator by default. + */ + locator?: Locator; + /** + * Closes the filebrowser when true. True by default. + */ + closeFb: boolean; + /** + * List of locators to mask in the screenshot. See + * https://playwright.dev/docs/api/class-page#page-screenshot-option-mask. + */ + mask?: Locator[]; +}; + +const DEFAULT_SNAPSHOT_OPTS: SnapshotOptions = { + closeFb: true +}; + +type CreateJobOptions = { + /** + * Name of the job. + */ + name: string; +}; + +const DEFAULT_CREATE_JOB_OPTS: CreateJobOptions = { + name: 'MyTestJob' +}; /** * Helper class for Jupyter Scheduler testing in JupyterLab */ export class SchedulerHelper { - constructor(readonly page: Page) {} + constructor( + readonly page: IJupyterLabPageFixture, + readonly testInfo: TestInfo + ) {} /** - * JupyterLab launcher "Notebook Jobs" card selector + * JupyterLab launcher "Notebook Jobs" card locator */ - get launcherCardSelector() { - return 'div.jp-LauncherCard[title="Notebook Jobs"]'; + get launcherCard() { + return this.page.locator(SELECTORS.LAUNCHER_CARD); } /** - * JupyterLab launcher "Notebook Jobs" card locator + * Locates notebook toolbar */ - get launcherCardLocator() { - return this.page.locator(this.launcherCardSelector); + get notebookToolbar() { + return this.page.locator(SELECTORS.NOTEBOOK_TOOLBAR); } /** - * JupyterLab notebook toolbar "Create a notebook job" button selector + * Locates "Create a notebook job" button in notebook toolbar */ - get notebookToolbarButtonSelector() { - return 'button.jp-ToolbarButtonComponent[data-command="scheduling:create-from-notebook"][title="Create a notebook job"]'; + get createJobTbutton() { + return this.page.locator(SELECTORS.CREATE_JOB_TBUTTON); } /** - * JupyterLab notebook toolbar "Create a notebook job" button locator + * Locates "Enable debugger" icon in notebook toolbar */ - get notebookToolbarButtonLocator() { - return this.page.locator(this.notebookToolbarButtonSelector); + get enableDebuggerTbutton() { + return this.page.locator(SELECTORS.ENABLE_DEBUGGER_TBUTTON); } /** - * JupyterLab File Browser right-click menu "Create Notebook Job" item selector + * Locates kernel name button in notebook toolbar */ - get filebrowserMenuItemSelector() { - return 'li[data-type="command"][data-command="scheduling:create-from-filebrowser"] >> div:has-text("Create Notebook Job")'; + get kernelNameTbutton() { + return this.page.locator(SELECTORS.KERNEL_NAME_TBUTTON); } /** - * JupyterLab File Browser right-click menu "Create Notebook Job" item locator + * Locates execution indicator icon in notebook toolbar */ - get filebrowserMenuItemLocator() { - return this.page.locator(this.filebrowserMenuItemSelector); + get executionIndicatorTbutton() { + return this.page.locator(SELECTORS.EXECUTION_INDICATOR_TBUTTON); + } + + /** + * Locates the previously created notebook's listing in the filebrowser. + */ + get notebookFbListing() { + if (this._nbName == null) { + throw Error( + 'Notebook was not created via scheduler.createNotebook() prior.' + ); + } + + return this.page + .locator('.jp-DirListing-item[data-file-type="notebook"]') + .getByText(this._nbName); } /** - * Notebook jobs panel selector + * Locates the column of timestamps in the list view. Used to mask this column + * during snapshot tests. */ - get timestampSelector() { - return 'td.MuiTableCell-body:has-text(" AM"), td.MuiTableCell-body:has-text(" PM")'; + get timestamp() { + return this.page.locator(SELECTORS.LIST_VIEW_TIMES); } /** - * Notebook jobs panel locator + * Opens create job view from the filebrowser context menu. */ - get timestampLocator() { - return this.page.locator(this.timestampSelector); + async openCreateJobFromFilebrowser() { + await this.page.sidebar.openTab('filebrowser'); + await this.notebookFbListing.click({ button: 'right' }); + await this.page.getByText('Create Notebook Job').click(); + await this._waitForCreateJobLoaded(); } + + /** + * Creates a job from the filebrowser context menu. Fills in default values + * for all required fields. See `CreateJobOptions` for more configuration + * options. + */ + async createJobFromFilebrowser(customOpts?: Partial) { + const opts: CreateJobOptions = { + ...DEFAULT_CREATE_JOB_OPTS, + ...customOpts + }; + await this.openCreateJobFromFilebrowser(); + await this.page.fill('input[name=jobName]', opts.name); + await this.page.click('button:has-text("Create")'); + } + + /** + * Opens create job view from the notebook toolbar button. + */ + async openCreateJobFromTbutton() { + await this.createJobTbutton.click(); + await this._waitForCreateJobLoaded(); + } + + /** + * Creates a notebook, optionally closing the tab after. + */ + async createNotebook(keepOpen = true) { + this._nbName = await this.page.notebook.createNew(); + await this.page.notebook.save(); + if (!keepOpen) { + await this.closeAllTabs(); + } + return this._nbName; + } + + /** + * Cleans up the test environment by deleting any created notebooks. + */ + async cleanup() { + if (this._nbName) { + await this.page.contents.deleteFile(this._nbName); + } + + await this.closeAllTabs(); + } + + /** + * Closes all tabs, handling dialogs if necessary. This is preferable to + * `page.activity.closeAll()`, which does not handle confirmation dialogs. + */ + async closeAllTabs() { + await this.page.evaluate(async () => { + await window.jupyterapp.commands.execute('application:close-all'); + }); + + // if a dialog opens, close it + try { + await this.page.waitForSelector('.jp-Dialog', { timeout: 200 }); + await this.page.click('.jp-Dialog .jp-mod-accept'); + } catch (e) { + return; + } + } + + /** + * Asserts a screenshot against the snapshot at `filename`. By default, this + * method closes the sidebar prior to taking the screenshot. See + * `SnapshotOptions` for more configuration options. + */ + async assertSnapshot( + filename: string, + customOpts?: Partial + ) { + const opts: SnapshotOptions = { ...DEFAULT_SNAPSHOT_OPTS, ...customOpts }; + if (opts.closeFb) { + await this.page.sidebar.close('left'); + } + + const target = + opts.locator || + this.page.locator('.jp-MainAreaWidget:not(.lm-mod-hidden)'); + await target.waitFor({ state: 'visible' }); + const screenshotArgs = { + mask: opts.mask + }; + expect(await target.screenshot(screenshotArgs)).toMatchSnapshot(filename); + } + + async standardizeListCreateTime() { + await this.page.route('**/scheduler/*', async (route, req) => { + if (req.url().includes('max_items')) { + const res = await route.fetch(); + const json = await res.json(); + json.jobs[0].create_time = 1; + route.fulfill({ + status: res.status(), + headers: res.headers(), + body: JSON.stringify(json) + }); + } + }); + } + + protected async _waitForCreateJobLoaded() { + await this.page.waitForSelector('text=Loading …', { state: 'hidden' }); + } + + protected _nbName: string | null = null; } diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts b/ui-tests/tests/jupyter_scheduler.spec.ts index 317c1098..5379f67c 100644 --- a/ui-tests/tests/jupyter_scheduler.spec.ts +++ b/ui-tests/tests/jupyter_scheduler.spec.ts @@ -1,12 +1,16 @@ import { expect, test } from '@jupyterlab/galata'; import { SchedulerHelper } from '../helpers/SchedulerHelper'; -const CREATE_FROM_NOTEBOOK_SNAPSHOT_FILENAME = 'create-view-from-toolbar.png'; -const CREATE_VIEW_SNAPSHOT_FILENAME = 'create-view-empty.png'; -const LAUNCHER_SNAPSHOT_FILENAME = 'launcher-with-scheduler.png'; -const LIST_VIEW_IN_PROGRESS_SNAPSHOT_FILENAME = 'list-view-in-progress.png'; -const NOTEBOOK_SNAPSHOT_FILENAME= 'notebook-with-createjob-button.png'; -const RIGHTCLICK_MENU_SNAPSHOT_FILENAME = 'filebrowser-notebook-rightclick-menu.png'; +enum FILENAMES { + LAUNCHER = 'launcher.png', + NOTEBOOK_TOOLBAR = 'notebook-toolbar.png', + FILEBROWSER_MENU = 'filebrowser-menu.png', + // TODO: resolve this inconsistency in our frontend code. One entry point + // includes the file extension in the job name, the other does not. + CREATE_VIEW_FROM_TOOLBAR = 'create-view-from-toolbar.png', + CREATE_VIEW_FROM_FILEBROWSER = 'create-view-from-filebrowser.png', + LIST_VIEW = 'list-view.png' +} /** * Don't load JupyterLab webpage before running the tests. @@ -14,126 +18,65 @@ const RIGHTCLICK_MENU_SNAPSHOT_FILENAME = 'filebrowser-notebook-rightclick-menu. */ test.use({ autoGoto: false }); -test.describe('Jupyter Scheduler integration tests for JupyterLab', () => { - let schedulerHelper: SchedulerHelper; - test.beforeEach(async ({ page }) => { - schedulerHelper = new SchedulerHelper(page); +test.describe('Jupyter Scheduler', () => { + let scheduler: SchedulerHelper; + + test.beforeEach(async ({ page }, testInfo) => { + scheduler = new SchedulerHelper(page, testInfo); await page.goto(); - await page.sidebar.close( - (await page.sidebar.getTabPosition('filebrowser')) ?? undefined - ); }); - test('"Notebook Jobs" card is visible in JupyterLab launcher', async ({ - page - }) => { - const launcher = page.locator('div[role="main"] >> text=Launcher'); - await launcher.waitFor(); - const launcherCard = schedulerHelper.launcherCardLocator; - - await expect(launcherCard).toBeVisible(); - expect(await page.screenshot()).toMatchSnapshot(LAUNCHER_SNAPSHOT_FILENAME); + test('shows card in launcher', async () => { + await expect(scheduler.launcherCard).toBeVisible(); + await scheduler.assertSnapshot(FILENAMES.LAUNCHER); }); - test('"Create a notebook job" button in notebook toolbar is visible', async ({ - page - }) => { - await page.notebook.createNew(); - await page - .locator('.jp-DebuggerBugButton[aria-disabled="false"]') - .waitFor(); - await page - .locator('.jp-Notebook-ExecutionIndicator[data-status="idle"]') - .waitFor(); - const createJobButton = schedulerHelper.notebookToolbarButtonLocator; - - await expect(createJobButton).toBeVisible(); - expect(await page.screenshot()).toMatchSnapshot(NOTEBOOK_SNAPSHOT_FILENAME); - await page.menu.clickMenuItem('File>Save Notebook'); - await page.click('button:has-text("Rename")'); + test('shows notebook toolbar button', async () => { + await scheduler.createNotebook(); + await expect(scheduler.createJobTbutton).toBeVisible(); + await scheduler.assertSnapshot(FILENAMES.NOTEBOOK_TOOLBAR, { + locator: scheduler.notebookToolbar, + mask: [ + scheduler.enableDebuggerTbutton, + scheduler.kernelNameTbutton, + scheduler.executionIndicatorTbutton + ] + }); }); - test('"Create a notebook job" button in notebook toolbar leads to "Create a Job" page', async ({ - page - }) => { - await page.sidebar.openTab('filebrowser'); - expect(await page.sidebar.isTabOpen('filebrowser')).toBeTruthy(); - await page.filebrowser.refresh(); - await page.dblclick('.jp-DirListing-item[data-file-type="notebook"]'); - await page.sidebar.close( - (await page.sidebar.getTabPosition('filebrowser')) ?? undefined - ); - const createJobButton = schedulerHelper.notebookToolbarButtonLocator; - await createJobButton.click(); + test('opens create job view from notebook toolbar', async ({ page }) => { + await scheduler.createNotebook(); + await scheduler.createJobTbutton.click(); await page.waitForSelector('text=Loading …', { state: 'hidden' }); - await page.waitForSelector('text=Saving Completed', { state: 'hidden' }); - expect(await page.screenshot()).toMatchSnapshot(CREATE_FROM_NOTEBOOK_SNAPSHOT_FILENAME); + await scheduler.assertSnapshot(FILENAMES.CREATE_VIEW_FROM_TOOLBAR); }); - test('"Create Notebook Job" item is visible when right clicking a notebook in File Browser', async ({ - page - }) => { + test('shows filebrowser menu item', async ({ page }) => { + await scheduler.createNotebook(); await page.sidebar.openTab('filebrowser'); - expect(await page.sidebar.isTabOpen('filebrowser')).toBeTruthy(); - await page.filebrowser.refresh(); - await page.click('.jp-DirListing-item[data-file-type="notebook"]', { - button: 'right' + await scheduler.notebookFbListing.click({ button: 'right' }); + const fbCtxMenu = page.locator('.lm-Menu'); + await scheduler.assertSnapshot(FILENAMES.FILEBROWSER_MENU, { + locator: fbCtxMenu, + closeFb: false }); - - expect(await page.menu.isAnyOpen()).toBe(true); - const righClickMenu = page.locator('ul.lm-Menu-content[role="menu"]'); - const createJobItem = schedulerHelper.filebrowserMenuItemLocator; - await expect(createJobItem).toBeVisible(); - expect(await righClickMenu.screenshot()).toMatchSnapshot( - RIGHTCLICK_MENU_SNAPSHOT_FILENAME - ); }); - test('"Create Notebook Job" button from File Browser right-click menu leads to "Create a Job" page', async ({ - page - }) => { - await page.sidebar.openTab('filebrowser'); - expect(await page.sidebar.isTabOpen('filebrowser')).toBeTruthy(); - await page.filebrowser.refresh(); - await page.click('.jp-DirListing-item[data-file-type="notebook"]', { - button: 'right' - }); - expect(await page.menu.isAnyOpen()).toBe(true); - const createJobItem = schedulerHelper.filebrowserMenuItemLocator; - await createJobItem.click(); - await page.waitForSelector('text=Loading …', { state: 'hidden' }); - await page.sidebar.close( - (await page.sidebar.getTabPosition('filebrowser')) ?? undefined - ); - - expect(await page.screenshot()).toMatchSnapshot(CREATE_VIEW_SNAPSHOT_FILENAME); + test('opens create job view from filebrowser menu item', async () => { + await scheduler.createNotebook(); + await scheduler.openCreateJobFromFilebrowser(); + await scheduler.assertSnapshot(FILENAMES.CREATE_VIEW_FROM_FILEBROWSER); }); - test('Create a job and see it in the list of jobs', async ({ page }) => { - await page.sidebar.openTab('filebrowser'); - await page.filebrowser.refresh(); - await page.click('.jp-DirListing-item[data-file-type="notebook"]', { - button: 'right' - }); - const createJobItem = schedulerHelper.filebrowserMenuItemLocator; - await createJobItem.click(); - await page.waitForSelector('text=Loading', { state: 'hidden' }); - - await page.fill('input[name=jobName]', 'MyTestJob'); - await page.click('button:has-text("Create")'); - const jobNameLink = page.getByText('MyTestJob', { exact: true }); - jobNameLink.waitFor(); - await page.sidebar.close( - (await page.sidebar.getTabPosition('filebrowser')) ?? undefined - ); - const timeStamp = schedulerHelper.timestampLocator; - const contentPanel = page.locator('#jp-main-content-panel'); + test('shows newly created job in job list view', async ({ page }) => { + await scheduler.createNotebook(); + await scheduler.createJobFromFilebrowser(); + await scheduler.standardizeListCreateTime(); + await scheduler.assertSnapshot(FILENAMES.LIST_VIEW); + }); - await expect(contentPanel).toHaveScreenshot(LIST_VIEW_IN_PROGRESS_SNAPSHOT_FILENAME, { - mask: [timeStamp], - maskColor: 'white', - maxDiffPixelRatio: 0.01 - }); + test.afterEach(async () => { + await scheduler.cleanup(); }); }); diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-empty-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-empty-linux.png deleted file mode 100644 index 160e9083..00000000 Binary files a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-empty-linux.png and /dev/null differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-from-filebrowser-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-from-filebrowser-linux.png new file mode 100644 index 00000000..2900aae1 Binary files /dev/null and b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-from-filebrowser-linux.png differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-from-toolbar-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-from-toolbar-linux.png index d8fdba79..fc74e585 100644 Binary files a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-from-toolbar-linux.png and b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/create-view-from-toolbar-linux.png differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/filebrowser-menu-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/filebrowser-menu-linux.png new file mode 100644 index 00000000..93b56f83 Binary files /dev/null and b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/filebrowser-menu-linux.png differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/filebrowser-notebook-rightclick-menu-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/filebrowser-notebook-rightclick-menu-linux.png deleted file mode 100644 index 82810c6e..00000000 Binary files a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/filebrowser-notebook-rightclick-menu-linux.png and /dev/null differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/launcher-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/launcher-linux.png new file mode 100644 index 00000000..3530d03b Binary files /dev/null and b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/launcher-linux.png differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/launcher-with-scheduler-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/launcher-with-scheduler-linux.png deleted file mode 100644 index 41d619de..00000000 Binary files a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/launcher-with-scheduler-linux.png and /dev/null differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/list-view-in-progress-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/list-view-in-progress-linux.png deleted file mode 100644 index 47525615..00000000 Binary files a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/list-view-in-progress-linux.png and /dev/null differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/list-view-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/list-view-linux.png new file mode 100644 index 00000000..3e40d79a Binary files /dev/null and b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/list-view-linux.png differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/notebook-toolbar-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/notebook-toolbar-linux.png new file mode 100644 index 00000000..d637364c Binary files /dev/null and b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/notebook-toolbar-linux.png differ diff --git a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/notebook-with-createjob-button-linux.png b/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/notebook-with-createjob-button-linux.png deleted file mode 100644 index 5db67ea2..00000000 Binary files a/ui-tests/tests/jupyter_scheduler.spec.ts-snapshots/notebook-with-createjob-button-linux.png and /dev/null differ