-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Python debugger test checking variables, stepping and stack for a basic python script. ### QA Notes All tests should pass. @:debug
- Loading branch information
1 parent
6fcb67e
commit 56424f5
Showing
5 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (C) 2024 Posit Software, PBC. All rights reserved. | ||
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import { expect } from '@playwright/test'; | ||
import { Code } from '../infra/code'; | ||
|
||
|
||
const GLYPH_AREA = '.margin-view-overlays>:nth-child'; | ||
const BREAKPOINT_GLYPH = '.codicon-debug-breakpoint'; | ||
const STOP = `.debug-toolbar .action-label[aria-label*="Stop"]`; | ||
|
||
const VIEWLET = 'div[id="workbench.view.debug"]'; | ||
const VARIABLE = `${VIEWLET} .debug-variables .monaco-list-row .expression`; | ||
|
||
const STEP_OVER = `.debug-toolbar .action-label[aria-label*="Step Over"]`; | ||
const STEP_INTO = `.debug-toolbar .action-label[aria-label*="Step Into"]`; | ||
const CONTINUE = `.debug-toolbar .action-label[aria-label*="Continue"]`; | ||
const STEP_OUT = `.debug-toolbar .action-label[aria-label*="Step Out"]`; | ||
|
||
const STACK_FRAME = `${VIEWLET} .monaco-list-row .stack-frame`; | ||
|
||
export interface IStackFrame { | ||
name: string; | ||
lineNumber: number; | ||
} | ||
|
||
/* | ||
* Reuseable Positron debug functionality for tests to leverage | ||
*/ | ||
export class Debug { | ||
|
||
constructor(private code: Code) { | ||
|
||
} | ||
|
||
async setBreakpointOnLine(lineNumber: number): Promise<void> { | ||
await expect(this.code.driver.page.locator(`${GLYPH_AREA}(${lineNumber})`)).toBeVisible(); | ||
await this.code.driver.page.locator(`${GLYPH_AREA}(${lineNumber})`).click({ position: { x: 5, y: 5 } }); | ||
await expect(this.code.driver.page.locator(BREAKPOINT_GLYPH)).toBeVisible(); | ||
} | ||
|
||
async startDebugging(): Promise<void> { | ||
await this.code.driver.page.keyboard.press('F5'); | ||
await expect(this.code.driver.page.locator(STOP)).toBeVisible(); | ||
} | ||
|
||
async getVariables(): Promise<string[]> { | ||
const variableLocators = await this.code.driver.page.locator(VARIABLE).all(); | ||
|
||
const variables: string[] = []; | ||
for (const variable of variableLocators) { | ||
const text = await variable.textContent(); | ||
if (text !== null) { | ||
variables.push(text); | ||
} | ||
} | ||
|
||
return variables; | ||
} | ||
|
||
async stepOver(): Promise<any> { | ||
await this.code.driver.page.locator(STEP_OVER).click(); | ||
} | ||
|
||
async stepInto(): Promise<any> { | ||
await this.code.driver.page.locator(STEP_INTO).click(); | ||
} | ||
|
||
async stepOut(): Promise<any> { | ||
await this.code.driver.page.locator(STEP_OUT).click(); | ||
} | ||
|
||
async continue(): Promise<any> { | ||
await this.code.driver.page.locator(CONTINUE).click(); | ||
} | ||
|
||
async getStack(): Promise<IStackFrame[]> { | ||
const stackLocators = await this.code.driver.page.locator(STACK_FRAME).all(); | ||
|
||
const stack: IStackFrame[] = []; | ||
for (const stackLocator of stackLocators) { | ||
const name = await stackLocator.locator('.file-name').textContent(); | ||
const lineNumberRaw = await stackLocator.locator('.line-number').textContent(); | ||
const lineNumber = lineNumberRaw ? parseInt(lineNumberRaw.split(':').shift() || '0', 10) : 0; | ||
stack.push({ name: name || '', lineNumber: lineNumber }); | ||
} | ||
|
||
return stack; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (C) 2024 Posit Software, PBC. All rights reserved. | ||
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import { expect } from '@playwright/test'; | ||
import { test, tags } from '../_test.setup'; | ||
import { join } from 'path'; | ||
import { Application } from '../../infra'; | ||
|
||
test.use({ | ||
suiteId: __filename | ||
}); | ||
|
||
test.describe('Python Debugging', { | ||
tag: [tags.DEBUG, tags.WEB, tags.WIN] | ||
}, () => { | ||
|
||
test('Python - Verify Basic Script Debugging [C1163800]', { tag: [tags.WIN] }, async function ({ app, python, openFile }) { | ||
|
||
await test.step('Open file, set breakpoint and start debugging', async () => { | ||
await openFile(join('workspaces', 'chinook-db-py', 'chinook-sqlite.py')); | ||
|
||
await app.workbench.debug.setBreakpointOnLine(6); | ||
|
||
await app.workbench.debug.startDebugging(); | ||
}); | ||
|
||
const requiredStrings = ["conn", "data_file_path", "os", "pd", "sqlite3"]; | ||
await test.step('Validate initial variable set', async () => { | ||
|
||
await validateExpectedVariables(app, requiredStrings); | ||
}); | ||
|
||
requiredStrings.push("cur"); | ||
await test.step('Step over and validate variable set with new member', async () => { | ||
await app.workbench.debug.stepOver(); | ||
|
||
await validateExpectedVariables(app, requiredStrings); | ||
}); | ||
|
||
await test.step('Validate current stack', async () => { | ||
const stack = await app.workbench.debug.getStack(); | ||
|
||
expect(stack[0]).toMatchObject({ | ||
name: "chinook-sqlite.py", | ||
lineNumber: 7 | ||
}); | ||
}); | ||
|
||
const internalRequiredStrings = ["columns", "copy", "data", "dtype", "index", "self"]; | ||
await test.step('Step over twice, then into and validate internal variables', async () => { | ||
await app.workbench.debug.stepOver(); | ||
await app.workbench.debug.stepOver(); | ||
await app.workbench.debug.stepInto(); | ||
|
||
await validateExpectedVariables(app, internalRequiredStrings); | ||
}); | ||
|
||
await test.step('Validate current internal stack', async () => { | ||
const stack = await app.workbench.debug.getStack(); | ||
|
||
expect(stack[0]).toMatchObject({ | ||
name: "frame.py", | ||
lineNumber: 702 | ||
}); | ||
|
||
expect(stack[1]).toMatchObject({ | ||
name: "chinook-sqlite.py", | ||
lineNumber: 9 | ||
}); | ||
}); | ||
|
||
await test.step('Step out, continue and wait completion', async () => { | ||
await app.workbench.debug.stepOut(); | ||
await app.workbench.debug.continue(); | ||
|
||
await expect(async () => { | ||
const stack = await app.workbench.debug.getStack(); | ||
expect(stack.length).toBe(0); | ||
}).toPass({ intervals: [1_000], timeout: 60000 }); | ||
}); | ||
}); | ||
}); | ||
|
||
async function validateExpectedVariables(app: Application, expectedVariables: string[]): Promise<void> { | ||
await expect(async () => { | ||
const variables = await app.workbench.debug.getVariables(); | ||
expectedVariables.forEach(prefix => { | ||
expect(variables.some(line => line.startsWith(prefix))).toBeTruthy(); | ||
}); | ||
}).toPass({ intervals: [1_000], timeout: 60000 }); | ||
} | ||
|