1 parent
aa4eb2f8ad
commit
e28fbbd9da
3 files changed
+144
-10
No files matched your search
@@ -7,6 +7,8 @@ import {
|
||||
eventually,
|
||||
fixtureRequest,
|
||||
login,
|
||||
loginAccount,
|
||||
loginPassword,
|
||||
scenario,
|
||||
} from "./helpers/console-browser.mjs";
|
||||
|
||||
@@ -120,6 +122,136 @@ test(
|
||||
}),
|
||||
);
|
||||
|
||||
for (const width of [320, 390]) {
|
||||
test(
|
||||
`password text remains editable beside its visibility button at ${width}px`,
|
||||
{ timeout: 25000 },
|
||||
(t) =>
|
||||
scenario(t, {}, async (page) => {
|
||||
await page.setViewportSize({ width, height: 844 });
|
||||
await page.goto(`${appOrigin}/register`);
|
||||
const field = page.getByLabel("密码 *", { exact: true });
|
||||
const password = "long-local-password-".repeat(4);
|
||||
const observations = [];
|
||||
for (const direction of ["ltr", "rtl"]) {
|
||||
// dir是浏览器渲染环境,不宣称产品新增了RTL语言入口或全站RTL支持。
|
||||
await page.locator("html").evaluate((element, dir) => {
|
||||
element.dir = dir;
|
||||
}, direction);
|
||||
await field.fill(password);
|
||||
await field.press("End");
|
||||
const point = await field.evaluate((input) => {
|
||||
const rect = input.getBoundingClientRect();
|
||||
const style = getComputedStyle(input);
|
||||
// 长密码滚到末端;点击实际文字编辑区末端,而不是假设按钮应有多少padding。
|
||||
return {
|
||||
x:
|
||||
style.direction === "rtl"
|
||||
? rect.left +
|
||||
parseFloat(style.borderLeftWidth) +
|
||||
parseFloat(style.paddingLeft) +
|
||||
1
|
||||
: rect.right -
|
||||
parseFloat(style.borderRightWidth) -
|
||||
parseFloat(style.paddingRight) -
|
||||
1,
|
||||
y: rect.top + rect.height / 2,
|
||||
overflowing: input.scrollWidth > input.clientWidth,
|
||||
};
|
||||
});
|
||||
assert.equal(
|
||||
point.overflowing,
|
||||
true,
|
||||
"Exercise a scrolled long password",
|
||||
);
|
||||
await page.mouse.click(point.x, point.y);
|
||||
assert.equal(
|
||||
await field.getAttribute("type"),
|
||||
"password",
|
||||
`${direction} text editing must not activate the visibility button`,
|
||||
);
|
||||
assert.equal(
|
||||
await field.evaluate((input) => input === document.activeElement),
|
||||
true,
|
||||
"A real click in the text editor must keep native input focus",
|
||||
);
|
||||
await page.keyboard.type("Z");
|
||||
const edited = await field.inputValue();
|
||||
assert.equal(edited.length, password.length + 1);
|
||||
assert.equal(edited.replace("Z", ""), password);
|
||||
assert.equal(
|
||||
await page.evaluate(
|
||||
() => document.documentElement.scrollWidth <= window.innerWidth,
|
||||
),
|
||||
true,
|
||||
);
|
||||
observations.push({ direction, textClickEditsWithoutToggling: true });
|
||||
}
|
||||
return { viewportWidth: width, editing: observations };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
"pending login disables password editing and visibility then restores native attributes",
|
||||
{ timeout: 25000 },
|
||||
(t) =>
|
||||
scenario(t, {}, async (page) => {
|
||||
let release;
|
||||
let received = false;
|
||||
const pending = new Promise((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
await page
|
||||
.context()
|
||||
.route(`${backendOrigin}/console/v1/login`, async (route) => {
|
||||
received = true;
|
||||
await pending;
|
||||
await route.fulfill({
|
||||
json: { status: 400, msg: "本地验证失败", data: {} },
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
});
|
||||
try {
|
||||
await page.goto(`${appOrigin}/login`);
|
||||
const field = page.getByLabel("密码", { exact: true });
|
||||
await page.getByLabel("账号", { exact: true }).fill(loginAccount);
|
||||
await field.fill(loginPassword);
|
||||
const toggle = field.locator("..").getByRole("button", {
|
||||
name: "显示密码",
|
||||
exact: true,
|
||||
});
|
||||
await page.getByRole("button", { name: "登录", exact: true }).click();
|
||||
await eventually(
|
||||
() => received,
|
||||
Boolean,
|
||||
"Login must reach the HTTP boundary",
|
||||
);
|
||||
assert.equal(await field.isDisabled(), true);
|
||||
assert.equal(await toggle.isDisabled(), true);
|
||||
const box = await toggle.boundingBox();
|
||||
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
|
||||
await page.keyboard.type("Z");
|
||||
assert.equal(await field.getAttribute("type"), "password");
|
||||
assert.equal(await field.inputValue(), loginPassword);
|
||||
release();
|
||||
await eventually(
|
||||
() => field.isEnabled(),
|
||||
Boolean,
|
||||
"Rejected login unlocks the form",
|
||||
);
|
||||
assert.equal(await toggle.isEnabled(), true);
|
||||
assert.equal(
|
||||
await field.getAttribute("aria-describedby"),
|
||||
"login-error",
|
||||
);
|
||||
return { disabledInputAndToggle: true, nativeAriaRestored: true };
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
async function contrast(locator) {
|
||||
return locator.evaluate((element) => {
|
||||
const style = getComputedStyle(element);
|
||||
|
||||
Reference in new issue
Block a user