// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ Copyright (c) 2017, the Perspective Authors. ┃ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ // ┃ This file is part of the Perspective library, distributed under the terms ┃ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ import { test, expect, PageView, ColumnSettingsSidebar } from "../helpers.ts"; test.beforeEach(async ({ page }) => { await page.goto("/rust/perspective-viewer/test/html/superstore-debug.html"); await page.evaluate(async () => { while (!window["__TEST_PERSPECTIVE_READY__"]) { await new Promise((x) => setTimeout(x, 10)); } }); }); export async function checkTab( columnSettingsSidebar: ColumnSettingsSidebar, active: boolean, expression: boolean, hasStyles: boolean = true, ) { await columnSettingsSidebar.container.waitFor({ state: "visible", }); let titles = await columnSettingsSidebar.tabTitle.all(); if (active) { if (expression) { if (hasStyles) { expect(titles.length).toBe(2); expect(await titles[0].getAttribute("id")).toBe("Style"); expect(await titles[1].getAttribute("id")).toBe("Attributes"); } else { expect(titles.length).toBe(1); expect(await titles[0].getAttribute("id")).toBe("Attributes"); } } else { expect(titles.length).toBe(1); expect(await titles[0].getAttribute("id")).toBe("Style"); } } else { if (expression) { expect(titles.length).toBe(1); expect(await titles[0].getAttribute("id")).toBe("Attributes"); } else { test.fail( true, "No settings exist for non-expression, inactive columns!", ); } } } test.describe("Column Settings Sidebar", () => { test("edit button > opens sidebar for active table and expression columns", async ({ page, }) => { let view = new PageView(page); let settingsPanel = await view.openSettingsPanel(); let inactiveColumns = settingsPanel.inactiveColumns; let activeColumns = settingsPanel.activeColumns; await settingsPanel.createNewExpression("expr", "true"); await inactiveColumns.container.waitFor({ state: "visible", }); let exprCol = await activeColumns.activateColumn("expr"); let firstCol = await activeColumns.getFirstVisibleColumn(); firstCol.editBtn.waitFor(); await firstCol.editBtn.click(); await checkTab(view.columnSettingsSidebar, true, false); await activeColumns.scrollToBottom(); exprCol.editBtn.waitFor(); await exprCol.editBtn.click(); await checkTab(view.columnSettingsSidebar, true, true); }); test("edit button > opens sidebar for inactive expression columns", async ({ page, }) => { let view = new PageView(page); let settingsPanel = await view.openSettingsPanel(); let inactiveColumns = settingsPanel.inactiveColumns; let activeColumns = settingsPanel.activeColumns; await settingsPanel.createNewExpression("expr", "true"); let exprCol = await inactiveColumns.getColumnByName("expr"); await activeColumns.toggleColumn("Row ID"); let rowId = await inactiveColumns.getColumnByName("Row ID"); expect(exprCol).toBeDefined(); expect(rowId).toBeDefined(); await exprCol.editBtn.waitFor(); await rowId.editBtn.waitFor({ state: "detached", timeout: 1000 }); await view.assureColumnSettingsClosed(); await exprCol.editBtn.click(); await checkTab(view.columnSettingsSidebar, false, true); }); test("tabs > switches between style and attributes tabs", async ({ page, }) => { let view = new PageView(page); let settingsPanel = await view.openSettingsPanel(); let sidebar = view.columnSettingsSidebar; let activeColumns = settingsPanel.activeColumns; let inactiveColumns = settingsPanel.inactiveColumns; await settingsPanel.createNewExpression("expr", "'string'"); await activeColumns.activateColumn("expr"); let col = await activeColumns.getColumnByName("expr"); await inactiveColumns.container.waitFor({ state: "hidden" }); await activeColumns.scrollToBottom(); await view.assureColumnSettingsOpen(col); await checkTab(sidebar, true, true, true); let tabs = await sidebar.tabTitle.all(); await tabs[1].click(); await sidebar.attributesTab.container.waitFor(); await tabs[0].click(); await sidebar.styleTab.container.waitFor(); }); test("state > persists sidebar when table data updates", async ({ page, }) => { await page.evaluate(async () => { // @ts-ignore let table = await window.__TEST_WORKER__.table({ x: [0] }); // @ts-ignore window.__TEST_TABLE__ = table; let viewer = document.querySelector("perspective-viewer"); await viewer?.load(table); }); let view = new PageView(page); let settingsPanel = await view.openSettingsPanel(); let col = settingsPanel.activeColumns.getFirstVisibleColumn(); await col.editBtn.click(); await view.columnSettingsSidebar.container.waitFor(); await page.evaluate(() => { // @ts-ignore window.__TEST_TABLE__.update({ x: [1] }); }); await page.waitForFunction(() => { // The Debug Styled test plugin renders its row count as text into // the viewer's light DOM; wait for it to reflect the update. const plugin = document .querySelector("perspective-viewer") ?.querySelector("perspective-viewer-debug-styled"); return (plugin?.textContent ?? "").includes("rows"); }); await expect(view.columnSettingsSidebar.container).toBeVisible(); }); test("layout > constrains width with long expression text", async ({ page, }) => { let view = new PageView(page); const MAX_WIDTH = 350; let checkWidth = async () => { let width = await view.columnSettingsSidebar.container.evaluate( (sidebar) => sidebar.getBoundingClientRect().width, ); expect(width).toEqual(MAX_WIDTH); }; let settings = await view.openSettingsPanel(); await settings.activeColumns.scrollToBottom(); await settings.addExpressionButton.click(); let editor = view.columnSettingsSidebar.attributesTab.expressionEditor; await editor.textarea.focus(); await editor.textarea.clear(); await editor.textarea.type( "'0000000000000000000000000000000000000000000000000000000000", ); await checkWidth(); await editor.textarea.clear(); await checkWidth(); }); test("tabs > preserves selected tab when toggling and saving expression", async ({ page, }) => { const view = new PageView(page); await view.restore({ expressions: { expr: "1234", }, columns: ["Row ID"], settings: true, }); const col = await view.settingsPanel.inactiveColumns.getColumnByName("expr"); await col.editBtn.click(); await view.columnSettingsSidebar.openTab("Attributes"); await checkTab(view.columnSettingsSidebar, false, true); const selectedTab = async () => { return await view.columnSettingsSidebar.selectedTab .locator(".tab-title") .getAttribute("id"); }; expect(await selectedTab()).toBe("Attributes"); await col.activeBtn.click(); await view.columnSettingsSidebar.container .locator(".tab-title#Style") .waitFor({ state: "visible" }); await checkTab(view.columnSettingsSidebar, true, true, true); expect(await selectedTab()).toBe("Attributes"); await view.columnSettingsSidebar.attributesTab.expressionEditor.textarea.clear(); await view.columnSettingsSidebar.attributesTab.expressionEditor.textarea.type( "'new expr value'", ); await view.columnSettingsSidebar.attributesTab.saveBtn.click(); expect(await selectedTab()).toBe("Attributes"); }); test("color range > resets to default when switching to a different number column", async ({ page, }) => { const view = new PageView(page); await view.restore({ plugin: "Debug Styled", columns: ["Row ID", "Postal Code"], settings: true, }); const defaultNegColor = "#ff471e"; let settingsPanel = await view.openSettingsPanel(); let activeColumns = settingsPanel.activeColumns; let firstCol = await activeColumns.getColumnByName("Row ID"); let secondCol = await activeColumns.getColumnByName("Postal Code"); // edit first number column await firstCol.editBtn.waitFor(); await firstCol.editBtn.click(); await checkTab(view.columnSettingsSidebar, true, false, false); // expect style tab is selected const selectedTab = async () => { return await view.columnSettingsSidebar.selectedTab .locator(".tab-title") .getAttribute("id"); }; expect(await selectedTab()).toBe("Style"); const getFgColorNeg = async () => { return view.columnSettingsSidebar.styleTab.container.locator( "input.neg_fg_color", ); }; let fgColorNeg = await getFgColorNeg(); expect(fgColorNeg).toBeTruthy(); expect(fgColorNeg).toBeVisible(); expect(fgColorNeg).toBeEditable(); expect(fgColorNeg).toHaveValue(defaultNegColor); // change -ve color from default await fgColorNeg.fill("#ffff00"); let negColorValue = await fgColorNeg.inputValue(); expect(negColorValue).toBe("#ffff00"); // edit second number column await secondCol.editBtn.waitFor(); await secondCol.editBtn.click(); // expect -ve color input on color range is present fgColorNeg = await getFgColorNeg(); expect(fgColorNeg).toBeVisible(); // expect -ve color input updates and switches back to default negColorValue = await fgColorNeg.inputValue(); expect(negColorValue).toBe(defaultNegColor); // edit first number column await firstCol.editBtn.waitFor(); await firstCol.editBtn.click(); // expect -ve color input on color range is present fgColorNeg = await getFgColorNeg(); expect(fgColorNeg).toBeVisible(); // expect -ve color input updates and switches back to the changed value negColorValue = await fgColorNeg.inputValue(); expect(negColorValue).toBe("#ffff00"); }); });