更新见下文
我想获取所有
<custom-tag>
节点的属性和<custom-text>
类型或<custom-number>
类型的节点子的innerText
devtools 中的简化版本是这样的
const headersMap = new Map();
var headers = Object.create(null);
document.querySelectorAll("h2").forEach(el => {
headers[el.id] = el.innerText
headersMap.set(el.id, el.innerText)
});
console.log("h2 count > 2 === " , (headersMap.size>2))
console.table(Array.from(headersMap, ([name, value]) => ({ name, value })));
对于API测试页面这看起来像这样
这是我编剧的尝试
test('API Testing - H2 count greater than 2', async ({ }) => {
await test.step('API Testing - get titles', async() => {
await page.goto('https://playwright.dev/docs/api-testing');
const headersList = await page.locator('h2');
var headersMap = await headersList.evaluateAll(node => {
const headersMap = new Map();
var headers = Object.create(null);
headers[node.id] = node.innerText
headersMap.set(node.id, node.innerText)
return headersMap;
});
await expect(headersMap.size).toBeGreaterThan(2);
page.pause();
});
});
但是它报告:
Error: expect(received).toBeGreaterThan(expected)
Matcher error: received value must be a number or bigint
Received has value: undefined
47 | return headersMap;
48 | });
> 49 | await expect(headersMap.size).toBeGreaterThan(2);
| ^
50 | page.pause();
ggorlen 的这个答案 已经有所帮助。
我创建了这个 html 演示,它更接近我的用例。我想测试按钮价格上涨是否会在不同地方产生正确的值。
test("Demo - price increase", async ({page}) => {
const url = "https://codepen.io/codemuggle/full/dygJLLV";
await page.goto(url, {waitUntil: "domcontentloaded"});
// the const lineItems is undefined / empty <-- why?
const lineItems = await page.locator("line-item").evaluateAll(nodes => {
var lineItems = []
// get values before price increase
nodes.forEach( node => {
lineItems.push({
"price": node.querySelector("line-item-price").innerText,
"qty": node.querySelector("input").value
});
});
return lineItems
});
const expected = [{"price": 30, "qty": 8},
{"price": 21, "qty": 12}];
// compare values with expected
expect(lineItems[0]).toEqual(expected[0]);
expect(lineItems[1]).toEqual(expected[1]);
});
使用
npx playwright test /tests/so/eval.spec.js --ui
运行测试会在 ui-mode 输出窗口中导致此错误
Error: expect(received).toEqual(expected) // deep equality
Expected: {"price": 30, "qty": 8}
Received: undefined
57 | const expected = [{"price": 30, "qty": 8},
58 | {"price": 21, "qty": 12}];
> 59 | expect(lineItems[0]).toEqual(expected[0]);
| ^
my_locator.evaluateAll()
返回地图必须改变什么?const lineItems
未定义?locator.evaluateAll()
时,你会在回调中得到一个数组。我会调用参数 nodes
而不是 node
所以你很清楚你需要迭代它。
第二个问题:地图不容易序列化。试试看:
const m = new Map();
m.set("hello", "world");
console.log(m.get("hello")); // => "world"
console.log(JSON.stringify(m)); // => {}
传入和传出
evaluate
调用的所有数据都将被反序列化/序列化,这将破坏您的地图。除非你有一些令人信服的理由使用地图,否则我只会使用一个普通的旧对象。
这里有一个选项:
import {expect, test} from "@playwright/test"; // ^1.30.0
test("playwright docs page has correct headers", async ({page}) => {
const url = "https://playwright.dev/docs/api-testing";
await page.goto(url, {waitUntil: "domcontentloaded"});
const headers = await page.locator("h2").evaluateAll(nodes =>
Object.fromEntries(
nodes.map(node => [node.id, node.textContent.trim()])
)
);
const expected = {
"writing-api-test": "Writing API Test",
"using-request-context": "Using request context",
"sending-api-requests-from-ui-tests": "Sending API requests from UI tests",
"reusing-authentication-state": "Reusing authentication state",
"context-request-vs-global-request": "Context request vs global request",
};
expect(headers).toEqual(expected);
});
请注意,您不需要
await
定位器定义,只需一个动作。通常使用.textContent
而不是.innerText
。修剪您的 HTML 字符串以规范化它们。注意那些标题中有趣的​
s(考虑只抓取文本节点并忽略<a>
)。无需 await
非定位器断言。
总而言之,上述方法并不是最佳实践,因为它没有使用 web first 断言。我会重写它以直接在定位器上使用
.toHaveText()
。
await expect(page.locator("h2")).toHaveText([
"Writing API Test",
"Using request context",
"Sending API requests from UI tests",
"Reusing authentication state",
"Context request vs global request",
]);
ID 可以单独测试,而且可能真的没有那么重要。您可以 poll 或 retry 等待谓词为真作为最后的手段,但如果您确定它们始终静态地显示在页面上,那么打破 web-first 规则可能是可以的。