Skip to content

Commit

Permalink
e2e-test: refactor select kernel (#5986)
Browse files Browse the repository at this point in the history
This PR updates the logic that selects the kernel in Notebooks to make
it easier to read and maintain.

### QA Notes
Ran all touched tests on PR.

@:notebooks @:plots @:data-explorer @:win
  • Loading branch information
midleman authored Jan 14, 2025
1 parent f455d33 commit 326c977
Show file tree
Hide file tree
Showing 10 changed files with 55 additions and 39 deletions.
8 changes: 6 additions & 2 deletions test/e2e/infra/test-runner/test-tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* -> Incorrect: `@tag`
*/
export enum TestTags {

// features and functionality
EDITOR_ACTION_BAR = '@editor-action-bar',
APPS = '@apps',
CONNECTIONS = '@connections',
Expand All @@ -32,7 +34,7 @@ export enum TestTags {
EDITOR = '@editor',
QUARTO = '@quarto',
NEW_PROJECT_WIZARD = '@new-project-wizard',
NOTEBOOK = '@notebook',
NOTEBOOKS = '@notebooks',
OUTLINE = '@outline',
OUTPUT = '@output',
PLOTS = '@plots',
Expand All @@ -42,7 +44,9 @@ export enum TestTags {
TEST_EXPLORER = '@test-explorer',
TOP_ACTION_BAR = '@top-action-bar',
VARIABLES = '@variables',
WEB = '@web',
WELCOME = '@welcome',

// platform
WEB = '@web',
WIN = '@win'
}
58 changes: 35 additions & 23 deletions test/e2e/pages/notebooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import { QuickAccess } from './quickaccess';
import { basename } from 'path';
import test, { expect } from '@playwright/test';


const KERNEL_LABEL = '.kernel-label';
const KERNEL_ACTION = '.kernel-action-view-item';
const SELECT_KERNEL_TEXT = 'Select Kernel';
const KERNEL_DROPDOWN = 'a.kernel-label';
const KERNEL_LABEL = '.codicon-notebook-kernel-select';
const DETECTING_KERNELS_TEXT = 'Detecting Kernels';
const NEW_NOTEBOOK_COMMAND = 'ipynb.newUntitledIpynb';
const CELL_LINE = '.cell div.view-lines';
Expand All @@ -24,45 +22,59 @@ const REVERT_AND_CLOSE = 'workbench.action.revertAndCloseActiveEditor';
const MARKDOWN_TEXT = '#preview';
const ACTIVE_ROW_SELECTOR = `.notebook-editor .monaco-list-row.focused`;


/*
* Reuseable Positron notebook functionality for tests to leverage. Includes selecting the notebook's interpreter.
*/
export class Notebooks {
kernelLabel = this.code.driver.page.locator(KERNEL_LABEL);
kernelDropdown = this.code.driver.page.locator(KERNEL_DROPDOWN);
frameLocator = this.code.driver.page.frameLocator(OUTER_FRAME).frameLocator(INNER_FRAME);
notebookProgressBar = this.code.driver.page.locator('[id="workbench\\.parts\\.editor"]').getByRole('progressbar');


constructor(private code: Code, private quickinput: QuickInput, private quickaccess: QuickAccess) { }

async selectInterpreter(kernelGroup: string, desiredKernel: string) {
await test.step(`Select notebook interpreter: ${desiredKernel}`, async () => {
async selectInterpreter(
kernelGroup: 'Python' | 'R',
desiredKernel = kernelGroup === 'Python'
? process.env.POSITRON_PY_VER_SEL!
: process.env.POSITRON_R_VER_SEL!
) {
await test.step(`Select kernel: ${desiredKernel}`, async () => {
await expect(this.notebookProgressBar).not.toBeVisible({ timeout: 30000 });
await expect(this.code.driver.page.locator(DETECTING_KERNELS_TEXT)).not.toBeVisible({ timeout: 30000 });

// Wait for the desired kernel to populate in dropdown, if no show then wait for "Select Kernel"
const kernelLabelLocator = this.code.driver.page.locator(KERNEL_LABEL);
try {
await expect(kernelLabelLocator).toHaveText(new RegExp(desiredKernel), { timeout: 10000 });
// 1. Try finding by text
await expect(this.kernelDropdown.filter({ hasText: desiredKernel })).toBeVisible({ timeout: 5000 });
this.code.logger.log(`Kernel: found by text: ${desiredKernel}`);
return;
} catch (e) {
await expect(kernelLabelLocator).toHaveText(new RegExp(SELECT_KERNEL_TEXT), { timeout: 10000 });
this.code.logger.log(`Kernel: not found by text: ${desiredKernel}`);
}

// Retrieve the matched text for conditional logic
const matchedText = await kernelLabelLocator.textContent() || '';

this.code.logger.log(`Matched text: ${matchedText}, Desired kernel: ${desiredKernel}`);
if (!new RegExp(desiredKernel).test(matchedText)) {
await this.code.driver.page.locator(KERNEL_ACTION).click();
await this.quickinput.waitForQuickInputOpened();
await this.quickinput.selectQuickInputElementContaining(kernelGroup);
await this.quickinput.selectQuickInputElementContaining(desiredKernel);
await this.quickinput.waitForQuickInputClosed();
try {
// 2. Try finding by label
const kernelLabelLocator = this.code.driver.page.locator(KERNEL_LABEL);
await expect(kernelLabelLocator).toHaveAttribute('aria-label', new RegExp(desiredKernel), { timeout: 5000 });
this.code.logger.log(`Kernel: found by label: ${desiredKernel}`);
return;
} catch (e) {
this.code.logger.log(`Kernel: not found by label: ${desiredKernel}`);
}

// wait for the kernel to load
await expect(this.code.driver.page.locator('.kernel-action-view-item').locator('.codicon-modifier-spin')).not.toBeVisible({ timeout: 30000 });
// 3. Open dropdown to select kernel
this.code.logger.log(`Kernel: opening dropdown to select: ${desiredKernel}`);

await this.code.driver.page.locator(KERNEL_DROPDOWN).click();
await this.quickinput.waitForQuickInputOpened();
await this.code.driver.page.getByText('Select Another Kernel...').click();
await this.quickinput.selectQuickInputElementContaining(`${kernelGroup} Environments...`);
await this.quickinput.selectQuickInputElementContaining(desiredKernel);
await this.quickinput.waitForQuickInputClosed();

// Wait for kernel initialization
await expect(this.code.driver.page.locator('.kernel-action-view-item .codicon-modifier-spin')).not.toBeVisible({ timeout: 30000 });
});
}

Expand Down
2 changes: 1 addition & 1 deletion test/e2e/pages/quickaccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export class QuickAccess {
intervals: [1000],
});

this.code.logger.log('QuickAccess: Command found and successfully executed.');
this.code.logger.log(`QuickAccess: ${commandId} ✓ success`);
await this.quickInput.selectQuickInputElement(0, keepOpen);
});
}
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/tests/action-bar/editor-action-bar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ test.describe('Editor Action Bar', {
});

test('Jupyter Notebook [C1080702]', {
tag: [tags.NOTEBOOK],
tag: [tags.NOTEBOOKS],
annotation: [{ type: 'info', description: 'electron test unable to interact with dropdown native menu' }],
}, async function ({ app, page }) {
await openNotebook(app, 'workspaces/large_r_notebook/spotify.ipynb');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ df2 = pd.DataFrame(data)`;

const filename = 'pandas-update-dataframe.ipynb';
await app.workbench.notebooks.openNotebook(join(app.workspacePathOrFolder, 'workspaces', 'data-explorer-update-datasets', filename));
await app.workbench.notebooks.selectInterpreter('Python Environments', process.env.POSITRON_PY_VER_SEL!);
await app.workbench.notebooks.selectInterpreter('Python', process.env.POSITRON_PY_VER_SEL!);
await app.workbench.notebooks.focusFirstCell();
await app.workbench.notebooks.executeActiveCell();

Expand Down
2 changes: 1 addition & 1 deletion test/e2e/tests/help/f1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ test.describe('F1 Help', {
await app.workbench.quickaccess.openDataFile(join(app.workspacePathOrFolder, 'workspaces', 'large_r_notebook', 'spotify.ipynb'));

// workaround
await app.workbench.notebooks.selectInterpreter('R Environments', process.env.POSITRON_R_VER_SEL!);
await app.workbench.notebooks.selectInterpreter('R', process.env.POSITRON_R_VER_SEL!);

await app.code.driver.page.locator('span').filter({ hasText: 'options(digits = 2)' }).locator('span').first().dblclick();

Expand Down
6 changes: 3 additions & 3 deletions test/e2e/tests/notebook/notebook-create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ test.use({
});

test.describe('Notebooks', {
tag: [tags.CRITICAL, tags.WEB, tags.WIN, tags.NOTEBOOK]
tag: [tags.CRITICAL, tags.WEB, tags.WIN, tags.NOTEBOOKS]
}, () => {
test.describe('Python Notebooks', () => {
test.beforeEach(async function ({ app, python }) {
await app.workbench.layouts.enterLayout('notebook');
await app.workbench.notebooks.createNewNotebook();
await app.workbench.notebooks.selectInterpreter('Python Environments', process.env.POSITRON_PY_VER_SEL!);
await app.workbench.notebooks.selectInterpreter('Python');
});

test.afterEach(async function ({ app }) {
Expand All @@ -43,7 +43,7 @@ test.describe('Notebooks', {
test.beforeEach(async function ({ app, r }) {
await app.workbench.layouts.enterLayout('notebook');
await app.workbench.notebooks.createNewNotebook();
await app.workbench.notebooks.selectInterpreter('R Environments', process.env.POSITRON_R_VER_SEL!);
await app.workbench.notebooks.selectInterpreter('R');
});

test.afterEach(async function ({ app }) {
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/tests/notebook/notebook-large-python.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test.use({

// Note that this test is too heavy to pass on web and windows
test.describe('Large Python Notebook', {
tag: [tags.NOTEBOOK, tags.WIN]
tag: [tags.NOTEBOOKS, tags.WIN]
}, () => {

test('Python - Large notebook execution [C983592]', async function ({ app, python }) {
Expand All @@ -22,7 +22,7 @@ test.describe('Large Python Notebook', {


await app.workbench.quickaccess.openDataFile(join(app.workspacePathOrFolder, 'workspaces', 'large_py_notebook', 'spotify.ipynb'));
await notebooks.selectInterpreter('Python Environments', process.env.POSITRON_PY_VER_SEL!);
await notebooks.selectInterpreter('Python');

await notebooks.runAllCells(120000);

Expand Down
4 changes: 2 additions & 2 deletions test/e2e/tests/plots/matplotlib-interact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test.use({
suiteId: __filename
});

test.describe('Matplotlib Interact', { tag: [tags.PLOTS, tags.NOTEBOOK] }, () => {
test.describe('Matplotlib Interact', { tag: [tags.PLOTS, tags.NOTEBOOKS] }, () => {

test('Python - Matplotlib Interact Test [C1067443]', {
tag: [tags.CRITICAL, tags.WEB, tags.WIN],
Expand All @@ -21,7 +21,7 @@ test.describe('Matplotlib Interact', { tag: [tags.PLOTS, tags.NOTEBOOK] }, () =>

await app.workbench.quickaccess.openDataFile(join(app.workspacePathOrFolder, 'workspaces', 'matplotlib', 'interact.ipynb'));

await notebooks.selectInterpreter('Python Environments', process.env.POSITRON_PY_VER_SEL!);
await notebooks.selectInterpreter('Python');

await notebooks.runAllCells();

Expand Down
6 changes: 3 additions & 3 deletions test/e2e/tests/variables/variables-notebook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ test.afterEach(async function ({ app }) {
});

test.describe('Variables Pane - Notebook', {
tag: [tags.CRITICAL, tags.WEB, tags.VARIABLES, tags.NOTEBOOK]
tag: [tags.CRITICAL, tags.WEB, tags.VARIABLES, tags.NOTEBOOKS]
}, () => {
test('Python - Verifies Variables pane basic function for notebook [C669188]', async function ({ app, python }) {
await app.workbench.notebooks.createNewNotebook();

// workaround issue where starting multiple interpreters in quick succession can cause startup failure
await app.code.wait(1000);

await app.workbench.notebooks.selectInterpreter('Python Environments', process.env.POSITRON_PY_VER_SEL!);
await app.workbench.notebooks.selectInterpreter('Python');
await app.workbench.notebooks.addCodeToFirstCell('y = [2, 3, 4, 5]');
await app.workbench.notebooks.executeCodeInCell();

Expand All @@ -41,7 +41,7 @@ test.describe('Variables Pane - Notebook', {
test('R - Verifies Variables pane basic function for notebook [C669189]', async function ({ app, r }) {
await app.workbench.notebooks.createNewNotebook();

await app.workbench.notebooks.selectInterpreter('R Environments', process.env.POSITRON_R_VER_SEL!);
await app.workbench.notebooks.selectInterpreter('R');
await app.workbench.notebooks.addCodeToFirstCell('y <- c(2, 3, 4, 5)');
await app.workbench.notebooks.executeCodeInCell();

Expand Down

0 comments on commit 326c977

Please sign in to comment.