Merge branch 'main' into test-behind-proxy-2
This commit is contained in:
commit
2c7f47a656
|
|
@ -259,3 +259,18 @@ def test_bing_is_working(page):
|
||||||
## Deploy to CI
|
## Deploy to CI
|
||||||
|
|
||||||
See the [guides for CI providers](./ci.md) to deploy your tests to CI/CD.
|
See the [guides for CI providers](./ci.md) to deploy your tests to CI/CD.
|
||||||
|
|
||||||
|
## Async Fixtures
|
||||||
|
|
||||||
|
If you want to use async fixtures, you can use the [`pytest-playwright-asyncio`](https://pypi.org/project/pytest-playwright-asyncio/) plugin.
|
||||||
|
Make sure to use `pytest-asyncio>=0.24.0` and make your tests use of [`loop_scope=sesion`](https://pytest-asyncio.readthedocs.io/en/latest/how-to-guides/run_session_tests_in_same_loop.html).
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pytest
|
||||||
|
from playwright.async_api import Page
|
||||||
|
|
||||||
|
@pytest.mark.asyncio(loop_scope="session")
|
||||||
|
async def test_foo(page: Page):
|
||||||
|
await page.goto("https://github.com")
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
|
||||||
13
package-lock.json
generated
13
package-lock.json
generated
|
|
@ -2171,14 +2171,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "4.6.2",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.0.tgz",
|
||||||
"integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
|
"integrity": "sha512-7n7KdUEtx/7Yl7I/WVAMZ1bEb0eVvXF3ummWTeLcs/9gvo9pJhuLdouSXGjdZ/MKD1acf1I272+X0RMua4/R3g==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vite": "^4.0.0 || ^5.0.0",
|
"vite": "^5.0.0",
|
||||||
"vue": "^3.2.25"
|
"vue": "^3.2.25"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -7876,7 +7877,7 @@
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.50.0-next",
|
"@playwright/experimental-ct-core": "1.50.0-next",
|
||||||
"@vitejs/plugin-vue": "^4.2.1"
|
"@vitejs/plugin-vue": "^5.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chromium-tip-of-tree",
|
"name": "chromium-tip-of-tree",
|
||||||
"revision": "1280",
|
"revision": "1282",
|
||||||
"installByDefault": false,
|
"installByDefault": false,
|
||||||
"browserVersion": "133.0.6850.0"
|
"browserVersion": "133.0.6864.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox",
|
"name": "firefox",
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webkit",
|
"name": "webkit",
|
||||||
"revision": "2111",
|
"revision": "2112",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"revisionOverrides": {
|
"revisionOverrides": {
|
||||||
"debian11-x64": "2105",
|
"debian11-x64": "2105",
|
||||||
|
|
|
||||||
|
|
@ -62,12 +62,8 @@ function yamlStringNeedsQuotes(str: string): boolean {
|
||||||
if (/^-\s/.test(str))
|
if (/^-\s/.test(str))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Strings that start with a special indicator character need quotes
|
// Strings containing ':' or '\n' followed by a space or at the end need quotes
|
||||||
if (/^[&*\],].*/.test(str))
|
if (/[\n:](\s|$)/.test(str))
|
||||||
return true;
|
|
||||||
|
|
||||||
// Strings containing ':' followed by a space or at the end need quotes
|
|
||||||
if (/:(\s|$)/.test(str))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Strings containing '#' preceded by a space need quotes (comment indicator)
|
// Strings containing '#' preceded by a space need quotes (comment indicator)
|
||||||
|
|
@ -78,21 +74,17 @@ function yamlStringNeedsQuotes(str: string): boolean {
|
||||||
if (/[\n\r]/.test(str))
|
if (/[\n\r]/.test(str))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Strings starting with '?' or '!' (directives) need quotes
|
// Strings starting with indicator characters or quotes need quotes
|
||||||
if (/^[?!]/.test(str))
|
if (/^[&*\],?!>|@"'#%]/.test(str))
|
||||||
return true;
|
|
||||||
|
|
||||||
// Strings starting with '>' or '|' (block scalar indicators) need quotes
|
|
||||||
if (/^[>|]/.test(str))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Strings starting with quotes need quotes
|
|
||||||
if (/^["']/.test(str))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Strings containing special characters that could cause ambiguity
|
// Strings containing special characters that could cause ambiguity
|
||||||
if (/[{}`]/.test(str))
|
if (/[{}`]/.test(str))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Non-string types recognized by YAML
|
||||||
|
if (!isNaN(Number(str)) || ['y', 'n', 'yes', 'no', 'true', 'false', 'on', 'off', 'null'].includes(str.toLowerCase()))
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.50.0-next",
|
"@playwright/experimental-ct-core": "1.50.0-next",
|
||||||
"@vitejs/plugin-vue": "^4.2.1"
|
"@vitejs/plugin-vue": "^5.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,9 @@ function indexTree<T extends TreeItem>(
|
||||||
selectedItem: T | undefined,
|
selectedItem: T | undefined,
|
||||||
expandedItems: Map<string, boolean | undefined>,
|
expandedItems: Map<string, boolean | undefined>,
|
||||||
autoExpandDepth: number,
|
autoExpandDepth: number,
|
||||||
isVisible?: (item: T) => boolean): Map<T, TreeItemData> {
|
isVisible: (item: T) => boolean = () => true): Map<T, TreeItemData> {
|
||||||
|
if (!isVisible(rootItem))
|
||||||
|
return new Map();
|
||||||
|
|
||||||
const result = new Map<T, TreeItemData>();
|
const result = new Map<T, TreeItemData>();
|
||||||
const temporaryExpanded = new Set<string>();
|
const temporaryExpanded = new Set<string>();
|
||||||
|
|
@ -328,9 +330,9 @@ function indexTree<T extends TreeItem>(
|
||||||
let lastItem: T | null = null;
|
let lastItem: T | null = null;
|
||||||
|
|
||||||
const appendChildren = (parent: T, depth: number) => {
|
const appendChildren = (parent: T, depth: number) => {
|
||||||
if (isVisible && !isVisible(parent))
|
|
||||||
return;
|
|
||||||
for (const item of parent.children as T[]) {
|
for (const item of parent.children as T[]) {
|
||||||
|
if (!isVisible(item))
|
||||||
|
continue;
|
||||||
const expandState = temporaryExpanded.has(item.id) || expandedItems.get(item.id);
|
const expandState = temporaryExpanded.has(item.id) || expandedItems.get(item.id);
|
||||||
const autoExpandMatches = autoExpandDepth > depth && result.size < 25 && expandState !== false;
|
const autoExpandMatches = autoExpandDepth > depth && result.size < 25 && expandState !== false;
|
||||||
const expanded = item.children.length ? expandState ?? autoExpandMatches : undefined;
|
const expanded = item.children.length ? expandState ?? autoExpandMatches : undefined;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
"vue-router": "^4.1.5"
|
"vue-router": "^4.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@vitejs/plugin-vue": "^5.2.0",
|
||||||
"@vue/tsconfig": "^0.5.1",
|
"@vue/tsconfig": "^0.5.1",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.2",
|
||||||
"vite": "^5.2.8",
|
"vite": "^5.2.8",
|
||||||
|
|
|
||||||
|
|
@ -436,6 +436,7 @@ it('should not auto play audio', {
|
||||||
}
|
}
|
||||||
}, async ({ page, browserName, isWindows }) => {
|
}, async ({ page, browserName, isWindows }) => {
|
||||||
it.fixme(browserName === 'webkit' && isWindows);
|
it.fixme(browserName === 'webkit' && isWindows);
|
||||||
|
it.skip(process.env.PW_CLOCK === 'frozen', 'no way to inject real setTimeout');
|
||||||
await page.route('**/*', async route => {
|
await page.route('**/*', async route => {
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|
|
||||||
|
|
@ -508,3 +508,57 @@ it('should handle long strings', async ({ page }) => {
|
||||||
- region: ${s}
|
- region: ${s}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should escape special yaml characters', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<a href="#">@hello</a>@hello
|
||||||
|
<a href="#">]hello</a>]hello
|
||||||
|
<a href="#">hello\n</a>
|
||||||
|
hello\n<a href="#">\n hello</a>\n hello
|
||||||
|
<a href="#">#hello</a>#hello
|
||||||
|
`);
|
||||||
|
|
||||||
|
await checkAndMatchSnapshot(page.locator('body'), `
|
||||||
|
- link "@hello"
|
||||||
|
- text: "@hello"
|
||||||
|
- link "]hello"
|
||||||
|
- text: "]hello"
|
||||||
|
- link "hello"
|
||||||
|
- text: hello
|
||||||
|
- link "hello"
|
||||||
|
- text: hello
|
||||||
|
- link "#hello"
|
||||||
|
- text: "#hello"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should escape special yaml values', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<a href="#">true</a>False
|
||||||
|
<a href="#">NO</a>yes
|
||||||
|
<a href="#">y</a>N
|
||||||
|
<a href="#">on</a>Off
|
||||||
|
<a href="#">null</a>NULL
|
||||||
|
<a href="#">123</a>123
|
||||||
|
<a href="#">-1.2</a>-1.2
|
||||||
|
<input type=text value="555">
|
||||||
|
`);
|
||||||
|
|
||||||
|
await checkAndMatchSnapshot(page.locator('body'), `
|
||||||
|
- link "true"
|
||||||
|
- text: "False"
|
||||||
|
- link "NO"
|
||||||
|
- text: "yes"
|
||||||
|
- link "y"
|
||||||
|
- text: "N"
|
||||||
|
- link "on"
|
||||||
|
- text: "Off"
|
||||||
|
- link "null"
|
||||||
|
- text: "NULL"
|
||||||
|
- link "123"
|
||||||
|
- text: "123"
|
||||||
|
- link "-1.2"
|
||||||
|
- text: "-1.2"
|
||||||
|
- textbox: "555"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -371,3 +371,27 @@ test('should work behind proxy', { annotation: { type: 'issue', description: 'ht
|
||||||
page.frameLocator('iframe.snapshot-visible[name=snapshot]').locator('button'),
|
page.frameLocator('iframe.snapshot-visible[name=snapshot]').locator('button'),
|
||||||
).toHaveText('Submit');
|
).toHaveText('Submit');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should filter actions tab on double-click', async ({ runUITest, server }) => {
|
||||||
|
const { page } = await runUITest({
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('pass', async ({ page }) => {
|
||||||
|
await page.goto('${server.EMPTY_PAGE}');
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByText('pass').dblclick();
|
||||||
|
|
||||||
|
const actionsTree = page.getByTestId('actions-tree');
|
||||||
|
await expect(actionsTree.getByRole('treeitem')).toHaveText([
|
||||||
|
/Before Hooks/,
|
||||||
|
/page.goto/,
|
||||||
|
/After Hooks/,
|
||||||
|
]);
|
||||||
|
await actionsTree.getByRole('treeitem', { name: 'page.goto' }).dblclick();
|
||||||
|
await expect(actionsTree.getByRole('treeitem')).toHaveText([
|
||||||
|
/page.goto/,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue