Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion e2e/global-setup.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { build } from "vite";

export default async function globalSetup() {
// Build once before workers start, including runs that skip fixture seeding.
await build({
configFile: fileURLToPath(new URL("../vite.config.js", import.meta.url)),
});

export default function globalSetup() {
if (process.env.DEBUG_UI_SEED_FIXTURES === "0") {
return;
}
Expand Down
148 changes: 148 additions & 0 deletions e2e/history-layout.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { expect, test } from "@playwright/test";

import { waitForStableUI } from "./support/debug-ui.js";

const stylesheet = new URL(
"../resources/assets/dist/css/debug.min.css",
import.meta.url,
);

function columns(query, mail) {
return [
["#", "yii-debug-col-num", ""],
["ID", "yii-debug-col-id", "tag"],
["Time", "", ""],
["Duration", "", ""],
["Memory", "", ""],
["IP", "yii-debug-col-ip", "ip"],
...(query ? [["Query", "yii-debug-col-num", "sqlCount"]] : []),
...(mail
? [["Mail", "yii-debug-col-num yii-debug-col-mail", "mailCount"]]
: []),
["Method", "", "method"],
["AJAX", "", "ajax"],
["URL", "", "url"],
];
}

function filter(attribute) {
if (!attribute) {
return "";
}

if (attribute === "method" || attribute === "ajax") {
return `<select name="Debug[${attribute}]"><option></option><option>COMMAND</option></select>`;
}

const className = attribute === "tag" ? "yii-debug-col-id-input" : "";

return `<input class="${className}" name="Debug[${attribute}]">`;
}

function rows(definitions, count) {
return Array.from({ length: count }, (_, index) => {
const url = `http://localhost/example/${"long-path-segment/".repeat(index)}`;
const values = {
"#": index + 1,
ID: `<a class="yii-debug-tag-link" href="#">${"a".repeat(32)}</a>`,
Time: "18:52:23",
Duration: '<span class="yii-debug-gauge">1234 ms</span>',
Memory: '<span class="yii-debug-gauge">123.456 MB</span>',
IP: index ? "2001:0db8:85a3:0000:0000:8a2e:0370:7334" : "::1",
Query: "3",
Mail: "0",
Method: "COMMAND",
AJAX: "Yes",
URL: `<span class="yii-debug-url-cell" title="${url}">${url}</span>`,
};

return `<tr>${definitions
.map(
([label, className]) =>
`<td class="${className}">${values[label]}</td>`,
)
.join("")}</tr>`;
}).join("");
}

for (const [adapter, query, mail] of [
["Yii2", true, true],
["Yii2 without Mail", true, false],
["Yii2 without Database", false, true],
["Yii3", false, false],
]) {
for (const theme of ["light", "dark"]) {
test(`${adapter} History keeps column widths stable in ${theme} theme`, async ({
page,
}) => {
const definitions = columns(query, mail);
const filterClass = adapter === "Yii3" ? "yii-debug-filter-cell" : "";

await page.setContent(`
<!doctype html>
<html lang="en" data-yii-debug-theme="${theme}">
<body class="yii-debug">
<main class="yii-debug-page">
<section class="yii-debug-main yii-debug-card">
<div class="yii-debug-grid yii-debug-grid-history">
<div class="yii-debug-table-wrap">
<table class="yii-debug-table">
<thead>
<tr>${definitions.map(([label, className]) => `<th class="${className}">${label}</th>`).join("")}</tr>
<tr class="${adapter === "Yii3" ? "" : "filters"}">${definitions.map(([, className, attribute]) => `<td class="${className} ${filterClass}">${filter(attribute)}</td>`).join("")}</tr>
</thead>
<tbody>${rows(definitions, 10)}</tbody>
</table>
</div>
</div>
</section>
</main>
</body>
</html>
`);
await page.addStyleTag({ path: stylesheet.pathname });
Comment thread
coderabbitai[bot] marked this conversation as resolved.
await waitForStableUI(page);

const headers = page.locator("thead th");
const widths = () =>
headers.evaluateAll((cells) =>
cells.map((cell) => cell.getBoundingClientRect().width),
);
const baseline = await widths();
const viewport = page.viewportSize();

expect(baseline.slice(0, 5)).toEqual([36, 276, 80, 100, 100]);
expect(baseline[5]).toBe(viewport.width <= 1366 ? 0 : 112);
expect(baseline.slice(-3, -1)).toEqual([128, 84]);

// Replacing the result rows models server-rendered filtering and paging.
for (const count of [50, 1, 0, 10]) {
await page.locator("tbody").evaluate(
(body, html) => {
body.innerHTML = html;
},
count
? rows(definitions, count)
: `<tr><td colspan="${definitions.length}">No results found.</td></tr>`,
);
expect(
await widths(),
`${count} results must preserve every column`,
).toEqual(baseline);

const overflow = await page.evaluate(() => ({
viewport: document.documentElement.clientWidth,
document: document.documentElement.scrollWidth,
previews: [...document.querySelectorAll(".yii-debug-url-cell")].every(
(element) =>
element.getBoundingClientRect().right <=
element.parentElement.getBoundingClientRect().right,
),
}));

expect(overflow.document).toBeLessThanOrEqual(overflow.viewport);
expect(overflow.previews).toBe(true);
}
});
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"check:contrast": "node tools/check-token-contrast.mjs",
"check:contrast:strict": "node tools/check-token-contrast.mjs --strict",
"test:events": "DEBUG_UI_SEED_FIXTURES=0 playwright test e2e/events.spec.js",
"test:e2e": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/db-controls.spec.js",
"test:e2e": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/db-controls.spec.js e2e/history-layout.spec.js",
"test:e2e:list": "DEBUG_UI_SEED_FIXTURES=0 playwright test --list",
"test:a11y": "playwright test e2e/accessibility.spec.js",
"test:security": "playwright test e2e/security.spec.js",
Expand All @@ -28,7 +28,7 @@
"test:visual:update": "DEBUG_UI_VISUAL_COMPARE=1 playwright test e2e/visual.spec.js --update-snapshots",
"test:perf": "DEBUG_UI_SEED_FIXTURES=0 playwright test e2e/large-data.spec.js --project=desktop-1440",
"test:perf:live": "playwright test e2e/live-performance.spec.js --project=desktop-1440",
"test:browser": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/db-controls.spec.js e2e/accessibility.spec.js e2e/visual.spec.js e2e/events.spec.js"
"test:browser": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/db-controls.spec.js e2e/history-layout.spec.js e2e/accessibility.spec.js e2e/visual.spec.js e2e/events.spec.js"
},
"engines": {
"node": "^22.12.0 || >=24.0.0"
Expand Down
2 changes: 1 addition & 1 deletion resources/assets/dist/css/debug.min.css

Large diffs are not rendered by default.

108 changes: 95 additions & 13 deletions resources/src/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -5458,37 +5458,119 @@

/**
* History rows are single-line instrument readouts: dense padding and
* no wrapping keep every capture scannable at a glance, while the URL
* cell's own ellipsis cap prevents runaway growth.
* no wrapping keep every capture scannable at a glance. Fixed instrument
* widths keep filters, pagination, and empty results from moving columns;
* URL alone takes the remaining space. Both adapters share the first six
* and last three columns, with optional counters between IP and Method.
*/
.yii-debug-grid-history .yii-debug-table {
--yii-debug-history-query-width: 0px;
--yii-debug-history-mail-width: 0px;
--yii-debug-history-fixed-width: calc(
916px + var(--yii-debug-history-query-width) + var(--yii-debug-history-mail-width)
);

min-width: calc(var(--yii-debug-history-fixed-width) + 180px);
table-layout: fixed;

&:has(thead th.yii-debug-col-num:not(:first-child, .yii-debug-col-mail)) {
--yii-debug-history-query-width: 66px;
}

&:has(thead th.yii-debug-col-mail) {
--yii-debug-history-mail-width: 56px;
}

thead tr:first-child {
> th:first-child {
width: 36px;
}

> th:nth-child(2) {
width: 276px;
}

> th:nth-child(3) {
width: 80px;
}

> th:nth-child(4),
> th:nth-child(5) {
width: 100px;
}

> th.yii-debug-col-ip {
width: 112px;
}

> th.yii-debug-col-num:not(:first-child, .yii-debug-col-mail) {
width: 66px;
}

> th.yii-debug-col-mail {
width: 56px;
}

> th:nth-last-child(3) {
width: 128px;
}

> th:nth-last-child(2) {
width: 84px;
}
}

tbody td,
tbody th {
padding: 8px 10px;
white-space: nowrap;
}

tbody td.yii-debug-col-ip {
white-space: normal;
overflow-wrap: anywhere;
}

/* A colspan empty state must not recreate responsively hidden columns. */
tbody:has(> tr:only-child > td[colspan]) {
display: table-caption;
caption-side: bottom;

td {
display: block;
}
}

.yii-debug-tag-link,
.yii-debug-url-cell {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}

.yii-debug-tag-link {
display: inline-block;
vertical-align: middle;
}
}

/**
* IP and MAIL are low-signal on laptop viewports (IP reads `::1` for
* every local capture, MAIL is almost always `0`) — hiding them at mid
* widths keeps the History grid inside its scroll wrap without an
* internal scrollbar. Both columns return on wide desktops.
* widths leaves more room for the URL. Narrow screens retain horizontal
* scrolling inside the table wrap. Both columns return on wide desktops.
*/
@media (width <= 1366px) {
.yii-debug-grid-history .yii-debug-table {
--yii-debug-history-fixed-width: calc(
804px + var(--yii-debug-history-query-width)
);
}

.yii-debug-grid-history .yii-debug-col-ip,
.yii-debug-grid-history .yii-debug-col-mail {
display: none;
}

/**
* The URL preview yields first: a tighter ellipsis cap lets the
* remaining instrument columns fit the scroll wrap without an
* internal scrollbar at laptop widths.
*/
.yii-debug-grid-history .yii-debug-url-cell {
max-width: 200px;
}
}

/**
Expand Down
Loading
Loading