diff --git a/test/automation/src/positron/positronPopups.ts b/test/automation/src/positron/positronPopups.ts index 06cbf0af93e..fe5e32864a4 100644 --- a/test/automation/src/positron/positronPopups.ts +++ b/test/automation/src/positron/positronPopups.ts @@ -19,6 +19,8 @@ const NOTIFICATION_TOAST = '.notification-toast'; */ export class PositronPopups { + toastLocator = this.code.driver.getLocator(NOTIFICATION_TOAST); + constructor(private code: Code) { } async popupCurrentlyOpen() { @@ -78,14 +80,36 @@ export class PositronPopups { } async waitForToastToDisappear() { this.code.logger.log('Waiting for toast to be detacted'); - const toastLocator = this.code.driver.getLocator(NOTIFICATION_TOAST); - await toastLocator.waitFor({ state: 'detached', timeout: 20000 }); + await this.toastLocator.waitFor({ state: 'detached', timeout: 20000 }); } async waitForToastToAppear() { this.code.logger.log('Waiting for toast to be attached'); - const toastLocator = this.code.driver.getLocator(NOTIFICATION_TOAST); - await toastLocator.waitFor({ state: 'attached', timeout: 20000 }); + await this.toastLocator.waitFor({ state: 'attached', timeout: 20000 }); + } + + async verifyToastDoesNotAppear(timeoutMs: number = 3000): Promise<void> { + const startTime = Date.now(); + + while (Date.now() - startTime < timeoutMs) { + const count = await this.toastLocator.count(); + if (count > 0) { + throw new Error('Toast appeared unexpectedly'); + } + + this.code.wait(1000); + } + + this.code.logger.log('Verified: Toast did not appear'); + } + + async closeAllToasts() { + const count = await this.toastLocator.count(); + this.code.logger.log(`Closing ${count} toasts`); + for (let i = 0; i < count; i++) { + await this.toastLocator.nth(i).hover(); + await this.code.driver.page.locator(`${NOTIFICATION_TOAST} .codicon-notifications-clear`).click(); + } } async waitForModalDialogBox() { diff --git a/test/automation/src/positron/positronVariables.ts b/test/automation/src/positron/positronVariables.ts index 96ca4dcc164..4be2f64134e 100644 --- a/test/automation/src/positron/positronVariables.ts +++ b/test/automation/src/positron/positronVariables.ts @@ -140,9 +140,9 @@ export class PositronVariables { async selectVariablesGroup(name: string) { await this.code.driver.page.locator(VARIABLES_GROUP_SELECTOR).click(); - await this.code.driver.page.locator('a.action-menu-item', { hasText: name }).isVisible(); + await this.code.driver.page.locator('a.action-menu-item', { hasText: name }).first().isVisible(); await this.code.wait(500); - await this.code.driver.page.locator('a.action-menu-item', { hasText: name }).click(); + await this.code.driver.page.locator('a.action-menu-item', { hasText: name }).first().click(); } async clickDatabaseIconForVariableRow(rowName: string) { diff --git a/test/e2e/features/data-explorer/data-explorer-python-pandas.test.ts b/test/e2e/features/data-explorer/data-explorer-python-pandas.test.ts index 837b5faabaa..f4e21183f8d 100644 --- a/test/e2e/features/data-explorer/data-explorer-python-pandas.test.ts +++ b/test/e2e/features/data-explorer/data-explorer-python-pandas.test.ts @@ -179,5 +179,64 @@ df2 = pd.DataFrame(data)`; }).toPass({ timeout: 60000 }); await app.workbench.positronLayouts.enterLayout('stacked'); + await app.workbench.positronDataExplorer.closeDataExplorer(); + }); + + test('Python - Open Data Explorer for the second time brings focus back [C1078833]', async function ({ app, python }) { + + const script = `import pandas as pd +from pydataset import data +Data_Frame = data('mtcars')`; + await app.workbench.positronConsole.executeCode('Python', script, '>>>'); + await app.workbench.quickaccess.runCommand('workbench.panel.positronVariables.focus'); + + if (app.web) { + await app.workbench.positronVariables.selectVariablesGroup(`${process.env.POSITRON_PY_VER_SEL!}`); + } + + await expect(async () => { + await app.workbench.positronVariables.doubleClickVariableRow('Data_Frame'); + await app.code.driver.getLocator('.label-name:has-text("Data: Data_Frame")').innerText(); + }).toPass(); + + // Now move focus out of the the data explorer pane + await app.workbench.editors.newUntitledFile(); + await app.workbench.quickaccess.runCommand('workbench.panel.positronVariables.focus'); + await app.workbench.positronVariables.doubleClickVariableRow('Data_Frame'); + + await expect(async () => { + await app.code.driver.getLocator('.label-name:has-text("Data: Data_Frame")').innerText(); + }).toPass(); + + await app.workbench.positronDataExplorer.closeDataExplorer(); + await app.workbench.quickaccess.runCommand('workbench.panel.positronVariables.focus'); + }); + + test('Python - Check blank spaces in data explorer [C1078835]', async function ({ app, python }) { + + const script = `import pandas as pd +df = pd.DataFrame({'x': ["a ", "a", " ", ""]})`; + await app.workbench.positronConsole.executeCode('Python', script, '>>>'); + + if (app.web) { + await app.workbench.positronVariables.selectVariablesGroup(`${process.env.POSITRON_PY_VER_SEL!}`); + } + + await expect(async () => { + await app.workbench.positronVariables.doubleClickVariableRow('df'); + await app.code.driver.getLocator('.label-name:has-text("Data: df")').innerText(); + }).toPass(); + + await expect(async () => { + const tableData = await app.workbench.positronDataExplorer.getDataExplorerTableData(); + + expect(tableData[0]).toStrictEqual({ 'x': 'a·' }); + expect(tableData[1]).toStrictEqual({ 'x': 'a' }); + expect(tableData[2]).toStrictEqual({ 'x': '···' }); + expect(tableData[3]).toStrictEqual({ 'x': '<empty>' }); + expect(tableData.length).toBe(4); + }).toPass({ timeout: 60000 }); + + await app.workbench.positronDataExplorer.closeDataExplorer(); }); }); diff --git a/test/e2e/features/data-explorer/data-explorer-r.test.ts b/test/e2e/features/data-explorer/data-explorer-r.test.ts index 0772ceafc49..1c6665738bd 100644 --- a/test/e2e/features/data-explorer/data-explorer-r.test.ts +++ b/test/e2e/features/data-explorer/data-explorer-r.test.ts @@ -76,8 +76,11 @@ test.describe('Data Explorer - R ', { }); - test('R - Open Data Explorer for the second time brings focus back [C701143]', async function ({ app, r }) { + test.skip('R - Open Data Explorer for the second time brings focus back [C701143]', { + annotation: [{ type: 'issue', description: 'https://github.com/posit-dev/positron/issues/5714' }] + }, async function ({ app, r }) { // Regression test for https://github.com/posit-dev/positron/issues/4197 + // and https://github.com/posit-dev/positron/issues/5714 const script = `Data_Frame <- mtcars`; await app.workbench.positronConsole.executeCode('R', script, '>'); await app.workbench.quickaccess.runCommand('workbench.panel.positronVariables.focus'); @@ -99,4 +102,24 @@ test.describe('Data Explorer - R ', { await app.workbench.positronDataExplorer.closeDataExplorer(); await app.workbench.quickaccess.runCommand('workbench.panel.positronVariables.focus'); }); + + test('R - Check blank spaces in data explorer [C1078834]', async function ({ app, r }) { + const script = `df = data.frame(x = c("a ", "a", " ", ""))`; + await app.workbench.positronConsole.executeCode('R', script, '>'); + + await expect(async () => { + await app.workbench.positronVariables.doubleClickVariableRow('df'); + await app.code.driver.getLocator('.label-name:has-text("Data: df")').innerText(); + }).toPass(); + + await expect(async () => { + const tableData = await app.workbench.positronDataExplorer.getDataExplorerTableData(); + + expect(tableData[0]).toStrictEqual({ 'x': 'a·' }); + expect(tableData[1]).toStrictEqual({ 'x': 'a' }); + expect(tableData[2]).toStrictEqual({ 'x': '···' }); + expect(tableData[3]).toStrictEqual({ 'x': '<empty>' }); + expect(tableData.length).toBe(4); + }).toPass({ timeout: 60000 }); + }); }); diff --git a/test/e2e/features/variables/variables-expanded.test.ts b/test/e2e/features/variables/variables-expanded.test.ts index 0bccaeae186..f860d5e8ada 100644 --- a/test/e2e/features/variables/variables-expanded.test.ts +++ b/test/e2e/features/variables/variables-expanded.test.ts @@ -10,20 +10,45 @@ test.use({ }); test.describe('Variables - Expanded View', { tag: [tags.WEB, tags.VARIABLES] }, () => { - test.beforeEach(async function ({ app, python }) { - await app.workbench.positronConsole.executeCode('Python', script, '>>>'); - await app.workbench.positronLayouts.enterLayout('fullSizedAuxBar'); + + test.afterEach(async function ({ app }) { + await app.workbench.positronLayouts.enterLayout('stacked'); + await app.workbench.positronConsole.barRestartButton.click(); }); - test('Python - should display children values and types when variable is expanded', async function ({ app }) { + test('Python - should display children values and types when variable is expanded [C1078836]', async function ({ app, python }) { const variables = app.workbench.positronVariables; + await app.workbench.positronConsole.executeCode('Python', script, '>>>'); + await app.workbench.positronLayouts.enterLayout('fullSizedAuxBar'); + await variables.expandVariable('df'); for (const variable of Object.keys(expectedData)) { const actualData = await variables.getVariableChildren(variable); expect(actualData).toEqual(expectedData[variable]); } }); + + test('R - getting large dataframe children should not cause problems [C1078837]', async function ({ app, r }) { + const variables = app.workbench.positronVariables; + + // workaround for https://github.com/posit-dev/positron/issues/5718 + await app.workbench.positronPopups.closeAllToasts(); + + await app.workbench.positronConsole.executeCode('R', 'df2 <- data.frame(b=rep(1:1000000))', '>'); + await app.workbench.positronLayouts.enterLayout('fullSizedAuxBar'); + + await variables.expandVariable('df2'); + const children = await variables.getVariableChildren('b', false); + + await app.workbench.positronPopups.verifyToastDoesNotAppear(); + + const childrenArray = Object.values(children); + + for (let i = 0; i < 10; i++) { + expect(childrenArray[i]).toEqual({ type: '', value: (i + 1).toString() }); + } + }); }); const script = `