From 6c5377d9ae5615effcf3877c3f2d289baa84a776 Mon Sep 17 00:00:00 2001 From: nstclair-cc <20171905+nstclair-cc@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:37:40 -0800 Subject: [PATCH 01/26] bring back "change points in table and check for autoscale" PT-#188370962 --- v3/cypress/e2e/axis.spec.ts | 14 +++++++------- v3/cypress/e2e/graph.spec.ts | 7 ++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/v3/cypress/e2e/axis.spec.ts b/v3/cypress/e2e/axis.spec.ts index 91e6ea04e..5a7a42a4b 100644 --- a/v3/cypress/e2e/axis.spec.ts +++ b/v3/cypress/e2e/axis.spec.ts @@ -56,7 +56,7 @@ context("Test graph axes with various attribute types", () => { // Verify the attribute was removed ah.verifyAxisTickLabels("bottom", arrayOfValues[7].values, true) }) - it("will add numeric attribute to x axis with undo/redo", () => { + it.only("will add numeric attribute to x axis with undo/redo", () => { // Adding the attribute cy.dragAttributeToTarget("table", arrayOfAttributes[2], "bottom") // LifeSpan => x-axis ah.verifyTickMarksDoNotExist("left") @@ -123,7 +123,7 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it("will add categorical attribute to x axis and categorical attribute to y axis with undo/redo", () => { + it.only("will add categorical attribute to x axis and categorical attribute to y axis with undo/redo", () => { cy.dragAttributeToTarget("table", arrayOfAttributes[7], "bottom") // Habitat => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[8], "left") // Diet => y-axis ah.verifyXAxisTickMarksDisplayed(true) @@ -158,7 +158,7 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it("will add categorical attribute to x axis and numeric attribute to y axis with undo/redo", () => { + it.only("will add categorical attribute to x axis and numeric attribute to y axis with undo/redo", () => { cy.dragAttributeToTarget("table", arrayOfAttributes[7], "bottom") // Habitat => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[5], "left") // Sleep => y-axis ah.verifyXAxisTickMarksDisplayed(true) @@ -192,7 +192,7 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it("will add numeric attribute to x axis and categorical attribute to y axis with undo/redo", () => { + it.only("will add numeric attribute to x axis and categorical attribute to y axis with undo/redo", () => { cy.dragAttributeToTarget("table", arrayOfAttributes[3], "bottom") // Height => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[7], "left") // Habitat => y-axis ah.verifyXAxisTickMarksDisplayed() @@ -227,7 +227,7 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it("will add numeric attribute to x axis and numeric attribute to y axis with undo/redo", () => { + it.only("will add numeric attribute to x axis and numeric attribute to y axis with undo/redo", () => { cy.dragAttributeToTarget("table", arrayOfAttributes[3], "bottom") // Height => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[4], "left") // Mass => y-axis ah.verifyXAxisTickMarksDisplayed() @@ -275,7 +275,7 @@ context("Test graph axes with various attribute types", () => { ah.removeAttributeFromAxis(arrayOfAttributes[7], "top") cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]").find(".sub-axis-wrapper").should("have.length", 1) }) - it("will test graph with numeric x-axis and two numeric y-attributes", () => { + it.only("will test graph with numeric x-axis and two numeric y-attributes", () => { cy.dragAttributeToTarget("table", arrayOfAttributes[2], "bottom") // Lifespan => x-axis cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]").find(".sub-axis-wrapper").should("have.length", 1) cy.dragAttributeToTarget("table", arrayOfAttributes[3], "left") // Height => left split @@ -308,7 +308,7 @@ context("Test graph axes with various attribute types", () => { cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]") .find(".sub-axis-wrapper").should("have.length", 1) }) - it("will adjust axis domain when points are changed to bars with undo/redo", () => { + it.only("will adjust axis domain when points are changed to bars with undo/redo", () => { // When there are no negative numeric values, such as in the case of Height, the domain for the primary // axis of a univariate plot showing bars should start at zero. cy.dragAttributeToTarget("table", arrayOfAttributes[3], "bottom") // Height => x-axis diff --git a/v3/cypress/e2e/graph.spec.ts b/v3/cypress/e2e/graph.spec.ts index 0c18bdfb6..7457a8b25 100644 --- a/v3/cypress/e2e/graph.spec.ts +++ b/v3/cypress/e2e/graph.spec.ts @@ -4,6 +4,7 @@ import { ComponentElements as c } from "../support/elements/component-elements" import { ToolbarElements as toolbar } from "../support/elements/toolbar-elements" import { CfmElements as cfm } from "../support/elements/cfm" import { ColorPickerPaletteElements as cpp} from "../support/elements/color-picker-palette" +import { AxisHelper as ah } from "../support/helpers/axis-helper" import graphRules from '../fixtures/graph-rules.json' const collectionName = "Mammals" @@ -149,11 +150,11 @@ context("Graph UI", () => { }) }) describe("graph inspector panel", () => { - // work on this later PT #188015800 - it.skip("change points in table and check for autoscale", () => { + it("change points in table and check for autoscale", () => { // create a graph with Lifespan (x-axis) and Height (y-axis) c.getComponentTitle("graph").should("have.text", collectionName) - cy.dragAttributeToTarget("table", "LifeSpan", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("LifeSpan", "bottom") // LifeSpan => x-axis cy.dragAttributeToTarget("table", "Height", "left") // change values in table so that points need rescale From 0e830cf4ee35425402206fc4405c8ac488794753 Mon Sep 17 00:00:00 2001 From: nstclair-cc <20171905+nstclair-cc@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:13:19 -0800 Subject: [PATCH 02/26] update to use the axis menu at the x-axis instead of dragging the attribute to the bottom axis --- v3/cypress/e2e/axis.spec.ts | 45 +++++++++++++++++++++--------------- v3/cypress/e2e/graph.spec.ts | 42 ++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/v3/cypress/e2e/axis.spec.ts b/v3/cypress/e2e/axis.spec.ts index 5a7a42a4b..312cae7f1 100644 --- a/v3/cypress/e2e/axis.spec.ts +++ b/v3/cypress/e2e/axis.spec.ts @@ -30,11 +30,10 @@ context("Test graph axes with various attribute types", () => { ah.verifyDefaultAxisLabel("bottom") ah.verifyDefaultAxisLabel("left") }) - // This test has become flaky (tick mark count is off) - // Skipping for now (PT-#188534232) - it.skip("will add categorical attribute to x axis with undo/redo", () => { + it("will add categorical attribute to x axis with undo/redo", () => { // Adding the attribute - cy.dragAttributeToTarget("table", arrayOfAttributes[7], "bottom") // Habitat => x-axis + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Habitat", "bottom") // Habitat => x-axis ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") ah.verifyXAxisTickMarksDisplayed(true) @@ -56,9 +55,10 @@ context("Test graph axes with various attribute types", () => { // Verify the attribute was removed ah.verifyAxisTickLabels("bottom", arrayOfValues[7].values, true) }) - it.only("will add numeric attribute to x axis with undo/redo", () => { + it("will add numeric attribute to x axis with undo/redo", () => { // Adding the attribute - cy.dragAttributeToTarget("table", arrayOfAttributes[2], "bottom") // LifeSpan => x-axis + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("LifeSpan", "bottom") // LifeSpan => x-axis ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") ah.verifyXAxisTickMarksDisplayed() @@ -123,8 +123,9 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it.only("will add categorical attribute to x axis and categorical attribute to y axis with undo/redo", () => { - cy.dragAttributeToTarget("table", arrayOfAttributes[7], "bottom") // Habitat => x-axis + it("will add categorical attribute to x axis and categorical attribute to y axis with undo/redo", () => { + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Habitat", "bottom") // Habitat => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[8], "left") // Diet => y-axis ah.verifyXAxisTickMarksDisplayed(true) ah.verifyXAxisGridLinesDisplayed(true) @@ -158,8 +159,9 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it.only("will add categorical attribute to x axis and numeric attribute to y axis with undo/redo", () => { - cy.dragAttributeToTarget("table", arrayOfAttributes[7], "bottom") // Habitat => x-axis + it("will add categorical attribute to x axis and numeric attribute to y axis with undo/redo", () => { + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Habitat", "bottom") // Habitat => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[5], "left") // Sleep => y-axis ah.verifyXAxisTickMarksDisplayed(true) ah.verifyXAxisGridLinesDisplayed(true) @@ -192,8 +194,9 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it.only("will add numeric attribute to x axis and categorical attribute to y axis with undo/redo", () => { - cy.dragAttributeToTarget("table", arrayOfAttributes[3], "bottom") // Height => x-axis + it("will add numeric attribute to x axis and categorical attribute to y axis with undo/redo", () => { + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Height", "bottom") // Height => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[7], "left") // Habitat => y-axis ah.verifyXAxisTickMarksDisplayed() ah.verifyXAxisGridLinesNotDisplayed() @@ -227,8 +230,9 @@ context("Test graph axes with various attribute types", () => { ah.verifyTickMarksDoNotExist("left") ah.verifyGridLinesDoNotExist("left") }) - it.only("will add numeric attribute to x axis and numeric attribute to y axis with undo/redo", () => { - cy.dragAttributeToTarget("table", arrayOfAttributes[3], "bottom") // Height => x-axis + it("will add numeric attribute to x axis and numeric attribute to y axis with undo/redo", () => { + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Height", "bottom") // Height => x-axis cy.dragAttributeToTarget("table", arrayOfAttributes[4], "left") // Mass => y-axis ah.verifyXAxisTickMarksDisplayed() ah.verifyYAxisTickMarksDisplayed() @@ -258,7 +262,8 @@ context("Test graph axes with various attribute types", () => { ah.verifyGridLinesDoNotExist("left") }) it("will split an axis into identical sub axes when categorical attribute is on opposite split", () => { - cy.dragAttributeToTarget("table", arrayOfAttributes[4], "bottom") // Mass => x-axis + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Mass", "bottom") // Mass => x-axis cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]").find(".sub-axis-wrapper").should("have.length", 1) cy.dragAttributeToTarget("table", arrayOfAttributes[7], "top") // Habitat => top split cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]").find(".sub-axis-wrapper").should("have.length", 3) @@ -275,8 +280,9 @@ context("Test graph axes with various attribute types", () => { ah.removeAttributeFromAxis(arrayOfAttributes[7], "top") cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]").find(".sub-axis-wrapper").should("have.length", 1) }) - it.only("will test graph with numeric x-axis and two numeric y-attributes", () => { - cy.dragAttributeToTarget("table", arrayOfAttributes[2], "bottom") // Lifespan => x-axis + it("will test graph with numeric x-axis and two numeric y-attributes", () => { + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("LifeSpan", "bottom") // LifeSpan => x-axis cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]").find(".sub-axis-wrapper").should("have.length", 1) cy.dragAttributeToTarget("table", arrayOfAttributes[3], "left") // Height => left split cy.dragAttributeToTarget("table", arrayOfAttributes[5], "yplus") // Sleep => left split @@ -308,10 +314,11 @@ context("Test graph axes with various attribute types", () => { cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]") .find(".sub-axis-wrapper").should("have.length", 1) }) - it.only("will adjust axis domain when points are changed to bars with undo/redo", () => { + it("will adjust axis domain when points are changed to bars with undo/redo", () => { // When there are no negative numeric values, such as in the case of Height, the domain for the primary // axis of a univariate plot showing bars should start at zero. - cy.dragAttributeToTarget("table", arrayOfAttributes[3], "bottom") // Height => x-axis + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Height", "bottom") // Height => x-axis ah.verifyXAxisTickMarksDisplayed() ah.verifyAxisTickLabel("bottom", "−0.5", 0) diff --git a/v3/cypress/e2e/graph.spec.ts b/v3/cypress/e2e/graph.spec.ts index 7457a8b25..631cb87a6 100644 --- a/v3/cypress/e2e/graph.spec.ts +++ b/v3/cypress/e2e/graph.spec.ts @@ -187,7 +187,8 @@ context("Graph UI", () => { cy.get("[data-testid=graph]").find("[data-testid=axis-bottom]").find(".tick").should("have.length", 29) }) it("hides and shows selected/unselected cases", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis cy.wait(500) // TODO: Add more thorough checks to make sure the cases are actually hidden and shown once Cypress is // configured to interact with the PixiJS canvas. For now, we just check that the buttons are disabled @@ -213,7 +214,8 @@ context("Graph UI", () => { // TODO: Add more thorough checks to make sure cases are actually hidden and shown, and the axes adjust // once Cypress is configured to interact with the PixiJS canvas. For now, we just check that the buttons // are disabled and enabled as expected. - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis cy.wait(500) graph.getHideShowButton().click() cy.get("[data-testid=display-selected-cases]").should("not.be.disabled") @@ -230,7 +232,8 @@ context("Graph UI", () => { cy.get("[data-testid=show-all-cases]").should("be.disabled") }) it("shows a warning when 'Display Only Selected Cases' is selected and no cases have been selected", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis cy.wait(500) cy.get("[data-testid=display-only-selected-warning]").should("not.exist") graph.getHideShowButton().click() @@ -243,7 +246,8 @@ context("Graph UI", () => { cy.get("[data-testid=display-only-selected-warning]").should("not.exist") }) it("shows parent visibility toggles when Show Parent Visibility Toggles option is selected", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis cy.wait(500) cy.get("[data-testid=parent-toggles-container]").should("not.exist") graph.getHideShowButton().click() @@ -334,7 +338,8 @@ context("Graph UI", () => { }) it("It adds a banner to the graph when Show Measures for Selection is activated", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis cy.get("[data-testid=measures-for-selection-banner]").should("not.exist") graph.getHideShowButton().click() cy.wait(500) @@ -453,7 +458,8 @@ context("Graph UI", () => { }) describe("graph bin configuration", () => { it("disables Point Size control when display type is bars", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis cy.wait(500) graph.getDisplayStylesButton().click() cy.get("[data-testid=point-size-slider]").should("not.have.attr", "aria-disabled") @@ -465,7 +471,8 @@ context("Graph UI", () => { cy.get("[data-testid=point-size-slider]").should("have.attr", "aria-disabled", "true") }) it("adds bin boundaries to plot when 'Group into Bins' is selected", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis cy.wait(500) cy.get("[data-testid=bin-ticks-graph-1]").should("not.exist") graph.getDisplayConfigButton().click() @@ -475,7 +482,8 @@ context("Graph UI", () => { cy.get("[data-testid=bin-ticks-graph-1]").should("exist") }) it("enables bin width and alignment options when 'Group into Bins' is selected", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis graph.getDisplayConfigButton().click() cy.get("[data-testid=graph-bin-width-setting]").should("not.exist") cy.get("[data-testid=graph-bin-alignment-setting]").should("not.exist") @@ -489,7 +497,8 @@ context("Graph UI", () => { cy.get("[data-testid=graph-bin-alignment-setting]").find("input").should("exist").should("have.value", "2") }) it("updates bin configuration when bin width and bin alignment values are changed", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis graph.getDisplayConfigButton().click() cy.get("[data-testid=bins-radio-button]").click() cy.wait(500) @@ -505,7 +514,8 @@ context("Graph UI", () => { cy.get("[data-testid=bin-ticks-graph-1]").find("path.draggable-bin-boundary-cover").should("have.length", 4) }) it("reverts bin width and bin alignment to default values for new value range when attribute is changed", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis graph.getDisplayConfigButton().click() cy.get("[data-testid=bins-radio-button]").click() cy.wait(500) @@ -519,7 +529,8 @@ context("Graph UI", () => { cy.get("[data-testid=graph-bin-alignment-setting]").find("input").should("have.value", "0") }) it("allows user to change bin width and alignment values by dragging the bin boundary lines", () => { - cy.dragAttributeToTarget("table", "Sleep", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Sleep", "bottom") // Sleep => x-axis graph.getDisplayConfigButton().click() cy.get("[data-testid=bins-radio-button]").click() cy.wait(500) @@ -547,7 +558,8 @@ context("Graph UI", () => { }) }) it("shows a bar graph for categorical attr on primary axis with 'Fuse Dots into Bars' checked", () => { - cy.dragAttributeToTarget("table", "Habitat", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Habitat", "bottom") // Habitat => x-axis cy.get("[data-testid=bar-cover]").should("not.exist") graph.getDisplayConfigButton().click() cy.get("[data-testid=bar-chart-checkbox]").find("input").should("exist").and("have.attr", "type", "checkbox") @@ -589,7 +601,8 @@ context("Graph UI", () => { }) }) it("shows a histogram when 'Group into Bins' and 'Fuse Dots into Bars' are both checked", () => { - cy.dragAttributeToTarget("table", "Height", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Height", "bottom") // Height => x-axis cy.get("[data-testid=bar-cover]").should("not.exist") graph.getDisplayConfigButton().click() cy.get("[data-testid=bins-radio-button]").click() @@ -599,7 +612,8 @@ context("Graph UI", () => { cy.wait(500) cy.get("[data-testid=bar-cover]").should("exist").and("have.length", 4) cy.wait(500) - cy.dragAttributeToTarget("table", "Speed", "bottom") + ah.openAxisAttributeMenu("bottom") + ah.selectMenuAttribute("Speed", "bottom") // Speed => x-axis cy.wait(500) cy.get("[data-testid=bar-cover]").should("exist").and("have.length", 6) graph.getDisplayConfigButton().click() From e9fbc5bb657efd9ee5f2938dc4b4101939894db0 Mon Sep 17 00:00:00 2001 From: nstclair-cc <20171905+nstclair-cc@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:28:01 -0800 Subject: [PATCH 03/26] skip autoscale test for now --- v3/cypress/e2e/graph.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/v3/cypress/e2e/graph.spec.ts b/v3/cypress/e2e/graph.spec.ts index 631cb87a6..b9200e03b 100644 --- a/v3/cypress/e2e/graph.spec.ts +++ b/v3/cypress/e2e/graph.spec.ts @@ -150,7 +150,8 @@ context("Graph UI", () => { }) }) describe("graph inspector panel", () => { - it("change points in table and check for autoscale", () => { + // this test is flaky. Skipping for now (PT-#188370962) + it.skip("change points in table and check for autoscale", () => { // create a graph with Lifespan (x-axis) and Height (y-axis) c.getComponentTitle("graph").should("have.text", collectionName) ah.openAxisAttributeMenu("bottom") From 6162b9a935e8d2ef2c765cacba6ece9ae06e3f1e Mon Sep 17 00:00:00 2001 From: eireland <7716653+eireland@users.noreply.github.com> Date: Fri, 15 Nov 2024 02:57:12 +0000 Subject: [PATCH 04/26] Increment the build number --- v3/build_number.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/build_number.json b/v3/build_number.json index 7409a67a4..29475445a 100644 --- a/v3/build_number.json +++ b/v3/build_number.json @@ -1 +1 @@ -{"buildNumber":2002} +{"buildNumber":2003} From 1e998bd7b2d810fb1e08eac8ced6928365184603 Mon Sep 17 00:00:00 2001 From: Kirk Swenson Date: Thu, 14 Nov 2024 21:10:35 -0800 Subject: [PATCH 05/26] fix: import of v2 documents with minimized components (#1622) --- v3/src/models/document/free-tile-row.ts | 6 ++++-- v3/src/v2/import-v2-document.ts | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/v3/src/models/document/free-tile-row.ts b/v3/src/models/document/free-tile-row.ts index c5a4b33c6..8dff56c08 100644 --- a/v3/src/models/document/free-tile-row.ts +++ b/v3/src/models/document/free-tile-row.ts @@ -66,6 +66,7 @@ export interface IFreeTileInRowOptions extends ITileInRowOptions { width?: number height?: number zIndex?: number + isMinimized?: boolean animateCreation?: boolean } export const isFreeTileInRowOptions = (options?: ITileInRowOptions): options is IFreeTileInRowOptions => @@ -141,9 +142,10 @@ export const FreeTileRow = TileRowModel }, insertTile(tileId: string, options?: ITileInRowOptions) { const { - x = 50, y = 50, width = undefined, height = undefined, zIndex = this.nextZIndex(), animateCreation = false + x = 50, y = 50, width = undefined, height = undefined, zIndex = this.nextZIndex(), + isMinimized = undefined, animateCreation = false } = isFreeTileInRowOptions(options) ? options : {} - self.tiles.set(tileId, { tileId, x, y, width, height, zIndex }) + self.tiles.set(tileId, { tileId, x, y, width, height, zIndex, isMinimized }) animateCreation && self.animateCreationTiles.add(tileId) }, removeTile(tileId: string) { diff --git a/v3/src/v2/import-v2-document.ts b/v3/src/v2/import-v2-document.ts index 9f01e137e..9c18945bf 100644 --- a/v3/src/v2/import-v2-document.ts +++ b/v3/src/v2/import-v2-document.ts @@ -40,13 +40,17 @@ export function importV2Document(v2Document: CodapV2Document) { if (row && tile) { const info = getTileComponentInfo(tile.content.type) if (info) { - const { left = 0, top = 0, width, height, zIndex } = v2Component.layout + const { + layout: { left = 0, top = 0, width, height: v2Height, isVisible, zIndex }, savedHeight + } = v2Component + const isMinimized = (!!savedHeight && savedHeight >= v2Height && !isVisible) || undefined + const height = savedHeight && isMinimized ? savedHeight : v2Height // only apply imported width and height to resizable tiles const _width = !info.isFixedWidth ? { width } : {} const _height = !info?.isFixedHeight ? { height } : {} const _zIndex = zIndex != null ? { zIndex } : {} if (zIndex != null && zIndex > maxZIndex) maxZIndex = zIndex - const layout: IFreeTileInRowOptions = { x: left, y: top, ..._width, ..._height, ..._zIndex } + const layout: IFreeTileInRowOptions = { x: left, y: top, ..._width, ..._height, ..._zIndex, isMinimized } newTile = content?.insertTileSnapshotInRow(tile, row, layout) } } From 4c4ffdaee700df0a38744f6114124596446e853c Mon Sep 17 00:00:00 2001 From: eireland <7716653+eireland@users.noreply.github.com> Date: Fri, 15 Nov 2024 05:10:46 +0000 Subject: [PATCH 06/26] Increment the build number --- v3/build_number.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/build_number.json b/v3/build_number.json index 29475445a..c9e987769 100644 --- a/v3/build_number.json +++ b/v3/build_number.json @@ -1 +1 @@ -{"buildNumber":2003} +{"buildNumber":2004} From 1c55fc4a943e817ef658c3497ec2fc6b0003896e Mon Sep 17 00:00:00 2001 From: Evangeline Ireland Date: Fri, 15 Nov 2024 11:22:01 -0800 Subject: [PATCH 07/26] 182991523 date type precision menu (#1620) * Implements precision for both numeric and dates. * fix attr format string for numerical * chore: code review tweaks --------- Co-authored-by: Kirk Swenson --- v3/src/components/case-table/use-columns.tsx | 4 +- v3/src/components/case-table/use-rows.ts | 8 +-- .../attribute-format-utils.tsx | 12 ++-- .../edit-attribute-properties-modal.tsx | 71 ++++++++++++++----- v3/src/models/data/attribute-types.ts | 3 +- v3/src/models/data/attribute.test.ts | 5 -- v3/src/models/data/attribute.ts | 16 +++-- 7 files changed, 75 insertions(+), 44 deletions(-) diff --git a/v3/src/components/case-table/use-columns.tsx b/v3/src/components/case-table/use-columns.tsx index 752870c2a..3a859e224 100644 --- a/v3/src/components/case-table/use-columns.tsx +++ b/v3/src/components/case-table/use-columns.tsx @@ -31,8 +31,8 @@ export const useColumns = ({ data, indexColumn }: IUseColumnsProps) => { const collection = data?.getCollection(collectionId) const attrs: IAttribute[] = collection ? getCollectionAttrs(collection, data) : [] const visible: IAttribute[] = attrs.filter(attr => attr && !caseMetadata?.isHidden(attr.id)) - return visible.map(({ id, name, type, userType, isEditable, hasFormula }) => - ({ id, name, type, userType, isEditable, hasFormula })) + return visible.map(({ id, name, type, userType, isEditable, hasFormula, precision }) => + ({ id, name, type, userType, isEditable, hasFormula, precision })) }, entries => { // column definitions diff --git a/v3/src/components/case-table/use-rows.ts b/v3/src/components/case-table/use-rows.ts index a87f3e0a0..5f5b30237 100644 --- a/v3/src/components/case-table/use-rows.ts +++ b/v3/src/components/case-table/use-rows.ts @@ -1,4 +1,3 @@ -import { format } from "d3" import { reaction } from "mobx" import { useCallback, useEffect, useRef } from "react" import { useDebouncedCallback } from "use-debounce" @@ -8,7 +7,6 @@ import { useDataSetContext } from "../../hooks/use-data-set-context" import { useLoggingContext } from "../../hooks/use-log-context" import { logMessageWithReplacement } from "../../lib/log-message" import { appState } from "../../models/app-state" -import { kDefaultFormatStr } from "../../models/data/attribute-types" import { isAddCasesAction, isRemoveCasesAction, isSetCaseValuesAction } from "../../models/data/data-set-actions" import { createCasesNotification } from "../../models/data/data-set-notifications" import { @@ -18,6 +16,7 @@ import { isSetIsCollapsedAction } from "../../models/shared/shared-case-metadata import { mstReaction } from "../../utilities/mst-reaction" import { onAnyAction } from "../../utilities/mst-utils" import { prf } from "../../utilities/profiler" +import { renderAttributeValue } from "../case-tile-common/attribute-format-utils" import { applyCaseValueChanges } from "../case-tile-common/case-tile-utils" import { kInputRowKey, symDom, TRow, TRowsChangeData } from "./case-table-types" import { useCollectionTableModel } from "./use-collection-table-model" @@ -86,9 +85,8 @@ export const useRows = (gridElement: HTMLDivElement | null) => { if (data && caseId && attr && cellSpan) { const strValue = data.getStrValue(caseId, attr.id) const numValue = data.getNumeric(caseId, attr.id) - const formatStr = attr.format || kDefaultFormatStr - const formatted = (numValue != null) && isFinite(numValue) ? format(formatStr)(numValue) : strValue - cellSpan.textContent = formatted ?? "" + const { value } = renderAttributeValue(strValue, numValue, attr) + cellSpan.textContent = value setCachedDomAttr(caseId, attr.id) } }) diff --git a/v3/src/components/case-tile-common/attribute-format-utils.tsx b/v3/src/components/case-tile-common/attribute-format-utils.tsx index 0daef1246..7e6f53dda 100644 --- a/v3/src/components/case-tile-common/attribute-format-utils.tsx +++ b/v3/src/components/case-tile-common/attribute-format-utils.tsx @@ -1,11 +1,11 @@ import { format } from "d3-format" import React from "react" import { IAttribute } from "../../models/data/attribute" -import { kDefaultFormatStr } from "../../models/data/attribute-types" +import { kDefaultNumPrecision } from "../../models/data/attribute-types" import { parseColor } from "../../utilities/color-utils" import { isStdISODateString } from "../../utilities/date-iso-utils" import { parseDate } from "../../utilities/date-parser" -import { DatePrecision, formatDate } from "../../utilities/date-utils" +import { formatDate } from "../../utilities/date-utils" import { kCaseTableBodyFont, kCaseTableHeaderFont, kMaxAutoColumnWidth, kMinAutoColumnWidth } from "../case-table/case-table-types" import { measureText } from "../../hooks/use-measure-text" @@ -24,8 +24,7 @@ export const getNumFormatter = (formatStr: string) => { } export function renderAttributeValue(str = "", num = NaN, attr?: IAttribute, key?: number) { - const { type, userType } = attr || {} - + const { type, userType, numPrecision, datePrecision } = attr || {} // colors const color = type === "color" || !userType ? parseColor(str, { colorNames: type === "color" }) : "" if (color) { @@ -41,7 +40,7 @@ export function renderAttributeValue(str = "", num = NaN, attr?: IAttribute, key // numbers if (isFinite(num)) { - const formatStr = attr?.format ?? kDefaultFormatStr + const formatStr = `.${numPrecision ?? kDefaultNumPrecision}~f` const formatter = getNumFormatter(formatStr) if (formatter) str = formatter(num) } @@ -57,8 +56,7 @@ export function renderAttributeValue(str = "", num = NaN, attr?: IAttribute, key if (isStdISODateString(str) || userType === "date" && str !== "") { const date = parseDate(str, true) if (date) { - // TODO: add precision support for date formatting - const formattedDate = formatDate(date, DatePrecision.None) + const formattedDate = formatDate(date, datePrecision) return { value: str, content: {formattedDate || `"${str}"`} diff --git a/v3/src/components/case-tile-common/attribute-menu/edit-attribute-properties-modal.tsx b/v3/src/components/case-tile-common/attribute-menu/edit-attribute-properties-modal.tsx index 4ebf015ba..ccadf5527 100644 --- a/v3/src/components/case-tile-common/attribute-menu/edit-attribute-properties-modal.tsx +++ b/v3/src/components/case-tile-common/attribute-menu/edit-attribute-properties-modal.tsx @@ -5,6 +5,7 @@ import { useDataSetContext } from "../../../hooks/use-data-set-context" import { logMessageWithReplacement } from "../../../lib/log-message" import { AttributeType, attributeTypes } from "../../../models/data/attribute" import { updateAttributesNotification } from "../../../models/data/data-set-notifications" +import { DatePrecision } from "../../../utilities/date-utils" import { uniqueName } from "../../../utilities/js-utils" import { t } from "../../../utilities/translation/translate" import { CodapModal } from "../../codap-modal" @@ -31,7 +32,7 @@ export const EditAttributePropertiesModal = ({ attributeId, isOpen, onClose }: I const [attributeName, setAttributeName] = useState(columnName) const [description, setDescription] = useState("") const [units, setUnits] = useState("") - const [precision, setPrecision] = useState("") + const [precision, setPrecision] = useState(attribute?.precision) const [userType, setUserType] = useState("none") const [editable, setEditable] = useState("yes") @@ -40,7 +41,7 @@ export const EditAttributePropertiesModal = ({ attributeId, isOpen, onClose }: I setAttributeName(attribute?.name || "attribute") setDescription(attribute?.description ?? "") setUnits(attribute?.units ?? "") - setPrecision(`${attribute?.precision ?? ""}`) + setPrecision(attribute?.precision) setUserType(attribute?.userType ?? "none") setEditable(attribute?.editable ? "yes" : "no") }, [attribute, isOpen]) @@ -63,8 +64,8 @@ export const EditAttributePropertiesModal = ({ attributeId, isOpen, onClose }: I if (userType !== (attribute.userType ?? "none")) { attribute.setUserType(userType === "none" ? undefined : userType) } - if (precision !== `${attribute?.precision ?? ""}`) { - attribute.setPrecision(precision ? +precision : undefined) + if (precision !== attribute.precision) { + attribute.setPrecision(precision) } if ((editable === "yes") !== attribute.editable) { attribute.setEditable(editable === "yes") @@ -100,6 +101,53 @@ export const EditAttributePropertiesModal = ({ attributeId, isOpen, onClose }: I { label: t("DG.AttrFormView.applyBtnTitle"), onClick: applyChanges, default: true } ] + function toDatePrecision(pStr: string) { + return !pStr || isFinite(Number(pStr)) ? undefined : pStr as DatePrecision + } + function toDatePrecisionStr(p: typeof precision) { + return p == null || typeof p === "number" ? "" : p + } + + function toNumPrecision(pStr: string) { + return isFinite(Number(pStr)) ? Number(pStr) : undefined + } + function toNumPrecisionStr(p: typeof precision) { + return p == null || typeof p === "string" ? "" : `${p}` + } + + const getPrecisionMenu = () => { + if (attribute?.type === "date" || userType === "date") { + return ( + + ) + } else { + return ( + + ) + } + } + return ( {t("DG.CaseTable.attributeEditor.precision")} - + {getPrecisionMenu()} {t("DG.CaseTable.attributeEditor.editable")} process.env.NODE_ENV !== "production" export const isProduction = () => process.env.NODE_ENV === "production" diff --git a/v3/src/models/data/attribute.test.ts b/v3/src/models/data/attribute.test.ts index ac0295e04..98dbe4b78 100644 --- a/v3/src/models/data/attribute.test.ts +++ b/v3/src/models/data/attribute.test.ts @@ -4,7 +4,6 @@ import { getSnapshot } from "mobx-state-tree" import { Attribute, IAttributeSnapshot, importValueToString, isAttributeType, isFormulaAttr, isValidFormulaAttr } from "./attribute" -import { kDefaultFormatStr } from "./attribute-types" describe("Attribute", () => { @@ -202,10 +201,6 @@ describe("Attribute", () => { expect(attribute.strValues).toEqual(["", "", "", "", "", ""]) expect(attribute.numValues).toEqual([NaN, NaN, NaN, NaN, NaN, NaN]) - expect(attribute.format).toBe(kDefaultFormatStr) - attribute.setPrecision(2) - expect(attribute.format).toBe(".2~f") - expect(attribute.description).toBeUndefined() attribute.setDescription("description") expect(attribute.description).toBe("description") diff --git a/v3/src/models/data/attribute.ts b/v3/src/models/data/attribute.ts index 67185875b..db9e16b4e 100644 --- a/v3/src/models/data/attribute.ts +++ b/v3/src/models/data/attribute.ts @@ -34,8 +34,9 @@ import { cachedFnFactory } from "../../utilities/mst-utils" import { Formula, IFormula } from "../formula/formula" import { applyModelChange } from "../history/apply-model-change" import { withoutUndo } from "../history/without-undo" -import { isDevelopment, isProduction, IValueType, kDefaultFormatStr } from "./attribute-types" +import { isDevelopment, isProduction, IValueType } from "./attribute-types" import { V2Model } from "./v2-model" +import { DatePrecision } from "../../utilities/date-utils" export interface ISetValueOptions { noInvalidate?: boolean @@ -71,7 +72,7 @@ export const Attribute = V2Model.named("Attribute").props({ userType: types.maybe(types.enumeration([...attributeTypes])), // userFormat: types.maybe(types.string), units: types.maybe(types.string), - precision: types.maybe(types.number), + precision: types.maybe(types.union(types.number, types.enumeration(Object.values(DatePrecision)))), deleteable: true, editable: true, formula: types.maybe(Formula), @@ -114,6 +115,12 @@ export const Attribute = V2Model.named("Attribute").props({ if (value == null || value === "") return NaN return Number(value) }, + get numPrecision() { + return typeof self.precision === "number" ? self.precision : undefined + }, + get datePrecision() { + return typeof self.precision === "string" ? self.precision : undefined + }, getEmptyCount: cachedFnFactory(() => { // Note that `self.changeCount` is absolutely not necessary here. However, historically, this function used to be // a MobX computed property, and `self.changeCount` was used to invalidate the cache. Also, there are tests @@ -240,9 +247,6 @@ export const Attribute = V2Model.named("Attribute").props({ return "categorical" }, - get format() { - return self.precision != null ? `.${self.precision}~f` : kDefaultFormatStr - }, get isEditable() { return self.editable && !self.hasFormula }, @@ -279,7 +283,7 @@ export const Attribute = V2Model.named("Attribute").props({ // setUserFormat(precision: string) { // self.userFormat = `.${precision}~f` // }, - setPrecision(precision?: number) { + setPrecision(precision?: number | DatePrecision) { self.precision = precision }, setDeleteable(deleteable: boolean) { From cdee5887efc135ca5fb3d8e888b9a84fab321bd5 Mon Sep 17 00:00:00 2001 From: eireland <7716653+eireland@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:22:15 +0000 Subject: [PATCH 08/26] Increment the build number --- v3/build_number.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/build_number.json b/v3/build_number.json index c9e987769..963f44fe8 100644 --- a/v3/build_number.json +++ b/v3/build_number.json @@ -1 +1 @@ -{"buildNumber":2004} +{"buildNumber":2005} From dc370b988e70e95dd53760e7d47f65e4e8403a56 Mon Sep 17 00:00:00 2001 From: Evangeline Ireland Date: Fri, 15 Nov 2024 11:34:12 -0800 Subject: [PATCH 09/26] 188497528 case tile filter formula editor (#1617) * Changes graph filter formula modal to use the new formula editor modal * Adds filter formula to map inspector panel Adds new formula editor modal to the map inspector panel Note that filtering isn't implemented * Update edit filter string * Updates cypress test because of the formula editor change * Adds a custom hook for inspector panel formula filter text. * Changes case table/card filter formula editor to use the new formula editor modal * refactored cypress elements to fix circula logic and keep selectors and function in their elements. * Fix linting errors in cypress tests * clean up --- .../e2e/formula/formula-component.spec.ts | 114 ++++----- v3/cypress/e2e/formula/formula-errors.spec.ts | 21 +- .../e2e/formula/formula-functions.spec.ts | 191 +++++++-------- .../formula-hierarchical-tables.spec.ts | 49 ++-- .../formula-special-characters.spec.ts | 227 +++++++++--------- v3/cypress/e2e/table.spec.ts | 3 +- v3/cypress/support/elements/slider-tile.ts | 8 +- v3/cypress/support/elements/table-tile.ts | 5 +- v3/cypress/support/helpers/formula-helper.ts | 43 ---- .../case-tile-common/hide-show-menu-list.tsx | 10 +- .../common/edit-filter-formula-modal.tsx | 101 -------- 11 files changed, 325 insertions(+), 447 deletions(-) delete mode 100644 v3/src/components/common/edit-filter-formula-modal.tsx diff --git a/v3/cypress/e2e/formula/formula-component.spec.ts b/v3/cypress/e2e/formula/formula-component.spec.ts index 76ae5b93e..0f517d7b6 100644 --- a/v3/cypress/e2e/formula/formula-component.spec.ts +++ b/v3/cypress/e2e/formula/formula-component.spec.ts @@ -1,80 +1,81 @@ import { FormulaHelper as fh } from "../../support/helpers/formula-helper" import { TableTileElements as table } from "../../support/elements/table-tile" +import { SliderTileElements as slider } from "../../support/elements/slider-tile" context("Formula Engine", () => { describe("Component Formula Tests", () => { it("Add and edit formula for a new attribute", () => { fh.visitURL("?sample=four&dashboard") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") - fh.addFormula("Formula", "a+1") - fh.verifyValues("Formula", [2, 3, 4, 4, 1]) - fh.checkFormulaExists("Formula", "a+1") - fh.editFormula("Formula", "a+2") - fh.verifyValues("Formula", [3, 4, 5, 5, 2]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") + table.addFormula("Formula", "a+1") + table.verifyFormulaValues("Formula", [2, 3, 4, 4, 1]) + table.checkFormulaExists("Formula", "a+1") + table.editFormula("Formula", "a+2") + table.verifyFormulaValues("Formula", [3, 4, 5, 5, 2]) }) it("Add and edit formula for an existing attribute", () => { fh.visitURL("?sample=four&dashboard") - fh.addFormula("b", "count(a)") - fh.verifyValues("b", [4, 4, 4, 4, 4]) - fh.checkFormulaExists("b", "count(a)") - fh.editFormula("b", "mean(a)") - fh.verifyValues("b", [2.25, 2.25, 2.25, 2.25, 2.25]) + table.addFormula("b", "count(a)") + table.verifyFormulaValues("b", [4, 4, 4, 4, 4]) + table.checkFormulaExists("b", "count(a)") + table.editFormula("b", "mean(a)") + table.verifyFormulaValues("b", [2.25, 2.25, 2.25, 2.25, 2.25]) }) it("Rename attribute and make sure formula updates", () => { fh.visitURL("?sample=four&dashboard") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") - fh.addFormula("Formula", "count(a)") - fh.verifyValues("Formula", [4, 4, 4, 4, 4]) - fh.renameAttribute("a", "x") - fh.checkFormulaExists("Formula", "count(x)") - fh.verifyValues("Formula", [4, 4, 4, 4, 4]) - fh.editFormula("Formula", "mean(x)") - fh.verifyValues("Formula", [2.25, 2.25, 2.25, 2.25, 2.25]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") + table.addFormula("Formula", "count(a)") + table.verifyFormulaValues("Formula", [4, 4, 4, 4, 4]) + table.renameAttribute("a", "x") + table.checkFormulaExists("Formula", "count(x)") + table.verifyFormulaValues("Formula", [4, 4, 4, 4, 4]) + table.editFormula("Formula", "mean(x)") + table.verifyFormulaValues("Formula", [2.25, 2.25, 2.25, 2.25, 2.25]) }) it("Delete attribute that a formula uses", () => { fh.visitURL("?sample=four&dashboard") - fh.addFormula("b", "count(a)") - fh.deleteAttribute("a") - fh.checkFormulaExists("b", "count(a)") - fh.verifyValues("b", [ + table.addFormula("b", "count(a)") + table.deleteAttribute("a") + table.checkFormulaExists("b", "count(a)") + table.verifyFormulaValues("b", [ "❌ Undefined symbol a", "❌ Undefined symbol a", "❌ Undefined symbol a", "❌ Undefined symbol a", "❌ Undefined symbol a" ]) - fh.editFormula("b", "5") - fh.verifyValues("b", [5, 5, 5, 5, 5]) + table.editFormula("b", "5") + table.verifyFormulaValues("b", [5, 5, 5, 5, 5]) }) it("Use slider variable with formula", () => { fh.visitURL("?sample=four&dashboard") - fh.addFormula("b", "count(a) + v1") - fh.verifyValues("b", [4.5, 4.5, 4.5, 4.5, 4.5]) - fh.changeSliderVariableName("v2") - fh.checkFormulaExists("b", "count(a) + v2") - fh.verifyValues("b", [4.5, 4.5, 4.5, 4.5, 4.5]) - fh.changeSliderValue("10") - fh.verifyValues("b", [14, 14, 14, 14, 14]) - fh.deleteSlider() - fh.checkFormulaExists("b", "count(a) + v2") + table.addFormula("b", "count(a) + v1") + table.verifyFormulaValues("b", [4.5, 4.5, 4.5, 4.5, 4.5]) + slider.changeVariableName("v2") + table.checkFormulaExists("b", "count(a) + v2") + table.verifyFormulaValues("b", [4.5, 4.5, 4.5, 4.5, 4.5]) + slider.changeVariableValue("10") + table.verifyFormulaValues("b", [14, 14, 14, 14, 14]) + slider.deleteSlider() + table.checkFormulaExists("b", "count(a) + v2") }) it("Add slider after using it in formula", () => { fh.visitURL("?sample=four") - fh.addFormula("b", "count(a) + v1") - fh.verifyValues("b", [ + table.addFormula("b", "count(a) + v1") + table.verifyFormulaValues("b", [ "❌ Undefined symbol v1", "❌ Undefined symbol v1", "❌ Undefined symbol v1", "❌ Undefined symbol v1", "❌ Undefined symbol v1" ]) - fh.addSlider() - fh.checkFormulaExists("b", "count(a) + v1") - fh.verifyValues("b", [4.5, 4.5, 4.5, 4.5, 4.5]) - fh.deleteSlider() - fh.verifyValues("b", [ + slider.addSlider() + table.checkFormulaExists("b", "count(a) + v1") + table.verifyFormulaValues("b", [4.5, 4.5, 4.5, 4.5, 4.5]) + slider.deleteSlider() + table.verifyFormulaValues("b", [ "❌ Undefined symbol v1", "❌ Undefined symbol v1", "❌ Undefined symbol v1", @@ -84,13 +85,14 @@ context("Formula Engine", () => { }) it("Formula in a new dataset", () => { fh.visitURL("") - fh.createNewDataset() - fh.insertCases(2, 4) - fh.addFormula("AttributeName", "10*10") - fh.verifyValues("AttributeName", [100, 100, 100, 100, 100]) - fh.addNewAttribute() - fh.addFormula("newAttr", "AttributeName+2") - fh.verifyValues("newAttr", [102, 102, 102, 102, 102]) + table.createNewTableFromToolShelf() + table.openIndexMenuForRow(2) + table.insertCases(4, "after") + table.addFormula("AttributeName", "10*10") + table.verifyFormulaValues("AttributeName", [100, 100, 100, 100, 100]) + table.addNewAttribute() + table.addFormula("newAttr", "AttributeName+2") + table.verifyFormulaValues("newAttr", [102, 102, 102, 102, 102]) }) }) describe("Add functions and values from Insert buttons", () => { @@ -98,8 +100,8 @@ context("Formula Engine", () => { const func = "abs" const funcCategory = "Arithmetic" fh.visitURL("?sample=four&dashboard") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") table.openAttributeMenu("Formula") table.selectMenuItemFromAttributeMenu("Edit Formula...") cy.get("[data-testid=formula-insert-function-button]").click() @@ -121,8 +123,8 @@ context("Formula Engine", () => { const func = "abs" const funcCategory = "Arithmetic" fh.visitURL("?sample=four&dashboard") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") table.openAttributeMenu("Formula") table.selectMenuItemFromAttributeMenu("Edit Formula...") cy.get("[data-testid=formula-insert-function-button]").click() @@ -139,8 +141,8 @@ context("Formula Engine", () => { it("Insert value into formula", () => { const value = "b" fh.visitURL("?sample=four&dashboard") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") table.openAttributeMenu("Formula") table.selectMenuItemFromAttributeMenu("Edit Formula...") cy.get("[data-testid=formula-insert-value-button]").click() diff --git a/v3/cypress/e2e/formula/formula-errors.spec.ts b/v3/cypress/e2e/formula/formula-errors.spec.ts index c1d27b501..e0c0e2291 100644 --- a/v3/cypress/e2e/formula/formula-errors.spec.ts +++ b/v3/cypress/e2e/formula/formula-errors.spec.ts @@ -1,21 +1,22 @@ import { FormulaHelper as fh } from "../../support/helpers/formula-helper" +import { TableTileElements as table } from "../../support/elements/table-tile" context("Formula Engine", () => { describe("Errors Formula Tests", () => { it("Check invalid functions", () => { fh.visitURL("?sample=four") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") - fh.addFormula("Formula", "count(aaa)") - fh.verifyValues("Formula", [ + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") + table.addFormula("Formula", "count(aaa)") + table.verifyFormulaValues("Formula", [ "❌ Undefined symbol aaa", "❌ Undefined symbol aaa", "❌ Undefined symbol aaa", "❌ Undefined symbol aaa", "❌ Undefined symbol aaa" ]) - fh.editFormula("Formula", "c(aaa)") - fh.verifyValues("Formula", [ + table.editFormula("Formula", "c(aaa)") + table.verifyFormulaValues("Formula", [ "❌ Undefined function c", "❌ Undefined function c", "❌ Undefined function c", @@ -23,16 +24,16 @@ context("Formula Engine", () => { "❌ Undefined function c" ]) // need to add {del} because CodeMirror auto-matches parentheses - fh.editFormula("Formula", "count(a{del}") - fh.verifyValues("Formula", [ + table.editFormula("Formula", "count(a{del}") + table.verifyFormulaValues("Formula", [ "❌ Syntax error: 'Parenthesis ) expected (char 8)'", "❌ Syntax error: 'Parenthesis ) expected (char 8)'", "❌ Syntax error: 'Parenthesis ) expected (char 8)'", "❌ Syntax error: 'Parenthesis ) expected (char 8)'", "❌ Syntax error: 'Parenthesis ) expected (char 8)'" ]) - fh.editFormula("Formula", "count(a)") - fh.verifyValues("Formula", [4, 4, 4, 4, 4]) + table.editFormula("Formula", "count(a)") + table.verifyFormulaValues("Formula", [4, 4, 4, 4, 4]) }) }) }) diff --git a/v3/cypress/e2e/formula/formula-functions.spec.ts b/v3/cypress/e2e/formula/formula-functions.spec.ts index c7d50976d..6e767bfdc 100644 --- a/v3/cypress/e2e/formula/formula-functions.spec.ts +++ b/v3/cypress/e2e/formula/formula-functions.spec.ts @@ -1,69 +1,70 @@ import { FormulaHelper as fh } from "../../support/helpers/formula-helper" +import { TableTileElements as table } from "../../support/elements/table-tile" context("Formula Engine", () => { it("Check arithmetic functions", () => { fh.visitURL("") fh.importFile("cypress/fixtures/formula.codap3") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "abs") - fh.addFormula("abs", "abs(num)") - fh.verifyValues("abs", [2, 1, 0, 2, 0.147, 0.976, 1.571, "Infinity", "Infinity", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "ceil") - fh.addFormula("ceil", "ceil(num)") - fh.verifyValues("ceil", [2, 1, 0, "−2", 0, 1, "−1", "Infinity", "-Infinity", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "exp") - fh.addFormula("exp", "exp(num)") - fh.verifyValues("exp", [7.389, 2.718, 1, 0.135, 0.864, 2.655, 0.208, "Infinity", 0, "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "floor") - fh.addFormula("floor", "floor(num)") - fh.verifyValues("floor", [2, 1, 0, "−2", "−1", 0, "−2", "Infinity", "-Infinity", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "frac") - fh.addFormula("frac", "frac(num)") - fh.verifyValues("frac", [0, 0, 0, 0, "−0.147", 0.976, "−0.571", "NaN", "NaN", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "ln") - fh.addFormula("ln", "ln(num)") - fh.verifyValues("ln", [0.693, 0, "-Infinity", "NaN", "NaN", "−0.024", "NaN", "Infinity", "NaN", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "log") - fh.addFormula("log", "log(num)") - fh.verifyValues("log", [0.301, 0, "-Infinity", "NaN", "NaN", "−0.01", "NaN", "Infinity", "NaN", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "pow") - fh.addFormula("pow", "pow(num, 2)") - fh.verifyValues("pow", [4, 1, 0, 4, 0.021, 0.953, 2.468, "Infinity", "Infinity", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "round") - fh.addFormula("round", "round(num)") - fh.verifyValues("round", [2, 1, 0, "−2", 0, 1, "−2", "Infinity", "-Infinity", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "sqrt") - fh.addFormula("sqrt", "sqrt(num)") - fh.verifyValues("sqrt", [1.414, 1, 0, "NaN", "NaN", 0.988, "NaN", "Infinity", "NaN", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "trunc") - fh.addFormula("trunc", "trunc(num)") - fh.verifyValues("trunc", [2, 1, 0, "−2", 0, 0, "−1", "Infinity", "-Infinity", "", ""]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "combinations") - fh.addFormula("combinations", "combinations(num, 1)") - fh.verifyValues("combinations", [2, 1, + table.addNewAttribute() + table.renameAttribute("newAttr", "abs") + table.addFormula("abs", "abs(num)") + table.verifyFormulaValues("abs", [2, 1, 0, 2, 0.147, 0.976, 1.571, "Infinity", "Infinity", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "ceil") + table.addFormula("ceil", "ceil(num)") + table.verifyFormulaValues("ceil", [2, 1, 0, "−2", 0, 1, "−1", "Infinity", "-Infinity", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "exp") + table.addFormula("exp", "exp(num)") + table.verifyFormulaValues("exp", [7.389, 2.718, 1, 0.135, 0.864, 2.655, 0.208, "Infinity", 0, "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "floor") + table.addFormula("floor", "floor(num)") + table.verifyFormulaValues("floor", [2, 1, 0, "−2", "−1", 0, "−2", "Infinity", "-Infinity", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "frac") + table.addFormula("frac", "frac(num)") + table.verifyFormulaValues("frac", [0, 0, 0, 0, "−0.147", 0.976, "−0.571", "NaN", "NaN", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "ln") + table.addFormula("ln", "ln(num)") + table.verifyFormulaValues("ln", [0.693, 0, "-Infinity", "NaN", "NaN", "−0.024", "NaN", "Infinity", "NaN", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "log") + table.addFormula("log", "log(num)") + table.verifyFormulaValues("log", [0.301, 0, "-Infinity", "NaN", "NaN", "−0.01", "NaN", "Infinity", "NaN", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "pow") + table.addFormula("pow", "pow(num, 2)") + table.verifyFormulaValues("pow", [4, 1, 0, 4, 0.021, 0.953, 2.468, "Infinity", "Infinity", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "round") + table.addFormula("round", "round(num)") + table.verifyFormulaValues("round", [2, 1, 0, "−2", 0, 1, "−2", "Infinity", "-Infinity", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "sqrt") + table.addFormula("sqrt", "sqrt(num)") + table.verifyFormulaValues("sqrt", [1.414, 1, 0, "NaN", "NaN", 0.988, "NaN", "Infinity", "NaN", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "trunc") + table.addFormula("trunc", "trunc(num)") + table.verifyFormulaValues("trunc", [2, 1, 0, "−2", 0, 0, "−1", "Infinity", "-Infinity", "", ""]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "combinations") + table.addFormula("combinations", "combinations(num, 1)") + table.verifyFormulaValues("combinations", [2, 1, "❌ k must be less than or equal to n", "❌ Positive integer value expected in function combinations", "❌ Positive integer value expected in function combinations", @@ -78,54 +79,56 @@ context("Formula Engine", () => { fh.visitURL("") fh.importFile("cypress/fixtures/formula.codap3") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "prev") - fh.addFormula("prev", "prev(num, 1)") - fh.verifyValues("prev", [1, 2, 1, 0, "−2", "−0.147", 0.976, "−1.571", "Infinity", "-Infinity", "foo"]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "next") - fh.addFormula("next", "next(num, 1)") - fh.verifyValues("next", [1, 0, "−2", "−0.147", 0.976, "−1.571", "Infinity", "-Infinity", "foo", "", 1]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "lookupByIndex") - fh.addFormula("lookupByIndex", `lookupByIndex("Formula", "num", 1)`) - fh.verifyValues("lookupByIndex", [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) - - fh.addNewAttribute() - fh.renameAttribute("newAttr", "lookupByKey") - fh.addFormula("lookupByKey", `lookupByKey("Formula", "num", "prev",1)`) - fh.verifyValues("lookupByKey", [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) + table.addNewAttribute() + table.renameAttribute("newAttr", "prev") + table.addFormula("prev", "prev(num, 1)") + table.verifyFormulaValues("prev", [1, 2, 1, 0, "−2", "−0.147", 0.976, "−1.571", "Infinity", "-Infinity", "foo"]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "next") + table.addFormula("next", "next(num, 1)") + table.verifyFormulaValues("next", [1, 0, "−2", "−0.147", 0.976, "−1.571", "Infinity", "-Infinity", "foo", "", 1]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "lookupByIndex") + table.addFormula("lookupByIndex", `lookupByIndex("Formula", "num", 1)`) + table.verifyFormulaValues("lookupByIndex", [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) + + table.addNewAttribute() + table.renameAttribute("newAttr", "lookupByKey") + table.addFormula("lookupByKey", `lookupByKey("Formula", "num", "prev",1)`) + table.verifyFormulaValues("lookupByKey", [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) }) it("Check logic functions", () => { fh.visitURL("") fh.importFile("cypress/fixtures/formula.codap3") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "boolean") - fh.addFormula("boolean", "boolean(3+4=7)") - fh.verifyValues("boolean", ["true", "true", "true", "true", "true", "true", "true", "true", "true", "true", "true"]) + table.addNewAttribute() + table.renameAttribute("newAttr", "boolean") + table.addFormula("boolean", "boolean(3+4=7)") + table.verifyFormulaValues("boolean", + ["true", "true", "true", "true", "true", "true", "true", "true", "true", "true", "true"]) }) it("Check other functions", () => { fh.visitURL("") fh.importFile("cypress/fixtures/formula.codap3") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "number") - fh.addFormula("number", `number("45")`) - fh.verifyValues("number", [45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45]) + table.addNewAttribute() + table.renameAttribute("newAttr", "number") + table.addFormula("number", `number("45")`) + table.verifyFormulaValues("number", [45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "random") - fh.addFormula("random", `if("random(1, 5) > 0 AND random(1, 5) < 6", "true", "false")`) - fh.verifyValues("random", ["true", "true", "true", "true", "true", "true", "true", "true", "true", "true", "true"]) + table.addNewAttribute() + table.renameAttribute("newAttr", "random") + table.addFormula("random", `if("random(1, 5) > 0 AND random(1, 5) < 6", "true", "false")`) + table.verifyFormulaValues("random", + ["true", "true", "true", "true", "true", "true", "true", "true", "true", "true", "true"]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "randomPick") - fh.addFormula("randomPick", `if("randomPick(1, 2, 3, 4, 5) > 0 AND randomPick(1, 2, 3, 4, 5) < 6", + table.addNewAttribute() + table.renameAttribute("newAttr", "randomPick") + table.addFormula("randomPick", `if("randomPick(1, 2, 3, 4, 5) > 0 AND randomPick(1, 2, 3, 4, 5) < 6", "true", "false")`) - fh.verifyValues("randomPick", ["true", "true", "true", "true", "true", "true", "true", "true", "true", + table.verifyFormulaValues("randomPick", ["true", "true", "true", "true", "true", "true", "true", "true", "true", "true", "true"]) }) }) diff --git a/v3/cypress/e2e/formula/formula-hierarchical-tables.spec.ts b/v3/cypress/e2e/formula/formula-hierarchical-tables.spec.ts index cddf68e43..b0891b1f3 100644 --- a/v3/cypress/e2e/formula/formula-hierarchical-tables.spec.ts +++ b/v3/cypress/e2e/formula/formula-hierarchical-tables.spec.ts @@ -1,55 +1,56 @@ import { FormulaHelper as fh } from "../../support/helpers/formula-helper" +import { TableTileElements as table } from "../../support/elements/table-tile" context("Formula Engine", () => { describe("Hierarchical Case Tables Formula Tests", () => { it("Check aggregate and non-aggregate formulae referencing a parent attribute in child collection", () => { fh.visitURL("") fh.importFile("cypress/fixtures/hierarchical-four.codap3") - fh.addNewAttribute(2) - fh.renameAttribute("newAttr", "Formula", 2) - fh.addFormula("Formula", "count(b)", 2) - fh.verifyValues("Formula", [ + table.addNewAttribute(2) + table.renameAttribute("newAttr", "Formula", 2) + table.addFormula("Formula", "count(b)", 2) + table.verifyFormulaValues("Formula", [ "❌ invalid reference to parent attribute 'b' within aggregate function", "❌ invalid reference to parent attribute 'b' within aggregate function", "❌ invalid reference to parent attribute 'b' within aggregate function", "❌ invalid reference to parent attribute 'b' within aggregate function", "❌ invalid reference to parent attribute 'b' within aggregate function" ], 2) - fh.editFormula("Formula", "b+1", 2) - fh.verifyValues("Formula", [2, 2, 2, 1, 1], 2) + table.editFormula("Formula", "b+1", 2) + table.verifyFormulaValues("Formula", [2, 2, 2, 1, 1], 2) }) it("Check aggregate and non-aggregate formulae referencing a child attribute in child collection", () => { fh.visitURL("") fh.importFile("cypress/fixtures/hierarchical-four.codap3") - fh.addNewAttribute(2) - fh.renameAttribute("newAttr", "Formula", 2) - fh.addFormula("Formula", "count(a)", 2) - fh.verifyValues("Formula", [3, 3, 3, 1, 1], 2) - fh.editFormula("Formula", "a+1", 2) - fh.verifyValues("Formula", [2, 3, 4, 4, 1], 2) + table.addNewAttribute(2) + table.renameAttribute("newAttr", "Formula", 2) + table.addFormula("Formula", "count(a)", 2) + table.verifyFormulaValues("Formula", [3, 3, 3, 1, 1], 2) + table.editFormula("Formula", "a+1", 2) + table.verifyFormulaValues("Formula", [2, 3, 4, 4, 1], 2) }) it("Check aggregate and non-aggregate formulae referencing a parent attribute in parent collection", () => { fh.visitURL("") fh.importFile("cypress/fixtures/hierarchical-four.codap3") - fh.addNewAttribute(1) - fh.renameAttribute("newAttr", "Formula", 1) - fh.addFormula("Formula", "count(b)", 1) - fh.verifyValues("Formula", [1, 1], 1) - fh.editFormula("Formula", "b+1", 1) - fh.verifyValues("Formula", [2, 1], 1) + table.addNewAttribute(1) + table.renameAttribute("newAttr", "Formula", 1) + table.addFormula("Formula", "count(b)", 1) + table.verifyFormulaValues("Formula", [1, 1], 1) + table.editFormula("Formula", "b+1", 1) + table.verifyFormulaValues("Formula", [2, 1], 1) }) it("Check aggregate and non-aggregate formulae referencing a child attribute in parent collection", () => { fh.visitURL("") fh.importFile("cypress/fixtures/hierarchical-four.codap3") - fh.addNewAttribute(1) - fh.renameAttribute("newAttr", "Formula", 1) - fh.addFormula("Formula", "a+1", 1) - fh.verifyValues("Formula", [ + table.addNewAttribute(1) + table.renameAttribute("newAttr", "Formula", 1) + table.addFormula("Formula", "a+1", 1) + table.verifyFormulaValues("Formula", [ "❌ invalid reference to child attribute 'a'", "❌ invalid reference to child attribute 'a'" ], 1) - fh.editFormula("Formula", "count(a)", 1) - fh.verifyValues("Formula", [3, 1], 1) + table.editFormula("Formula", "count(a)", 1) + table.verifyFormulaValues("Formula", [3, 1], 1) }) }) }) diff --git a/v3/cypress/e2e/formula/formula-special-characters.spec.ts b/v3/cypress/e2e/formula/formula-special-characters.spec.ts index bbe88a1ee..64a651dd1 100644 --- a/v3/cypress/e2e/formula/formula-special-characters.spec.ts +++ b/v3/cypress/e2e/formula/formula-special-characters.spec.ts @@ -1,150 +1,151 @@ import { FormulaHelper as fh } from "../../support/helpers/formula-helper" +import { TableTileElements as table } from "../../support/elements/table-tile" context("Formula Engine", () => { describe("Special Characters in Formula Tests", () => { it("Check backtick in symbol name", () => { fh.visitURL("?sample=four") - fh.renameAttribute("b", "x`yz") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula1") - fh.addFormula("Formula1", "`x\\`yz`+1") - fh.verifyValues("Formula1", [2, 2, 2, 1, 1]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula2") - fh.addFormula("Formula2", "Formula1+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) - fh.renameAttribute("Formula1", "Formula`1") - fh.checkFormulaExists("Formula2", "`Formula\\`1`+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("b", "x`yz") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula1") + table.addFormula("Formula1", "`x\\`yz`+1") + table.verifyFormulaValues("Formula1", [2, 2, 2, 1, 1]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula2") + table.addFormula("Formula2", "Formula1+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("Formula1", "Formula`1") + table.checkFormulaExists("Formula2", "`Formula\\`1`+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) }) it("Check backslash symbol in attribute", () => { fh.visitURL("?sample=four") - fh.renameAttribute("b", "x\\yz") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula1") - fh.addFormula("Formula1", "`x\\yz`+1") - fh.verifyValues("Formula1", [2, 2, 2, 1, 1]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula2") - fh.addFormula("Formula2", "Formula1+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) - fh.renameAttribute("Formula1", "Formula\\1") - fh.checkFormulaExists("Formula2", "`Formula\\\\1`+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("b", "x\\yz") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula1") + table.addFormula("Formula1", "`x\\yz`+1") + table.verifyFormulaValues("Formula1", [2, 2, 2, 1, 1]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula2") + table.addFormula("Formula2", "Formula1+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("Formula1", "Formula\\1") + table.checkFormulaExists("Formula2", "`Formula\\\\1`+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) }) it("Check backslash and backtick symbols in attribute", () => { fh.visitURL("?sample=four") - fh.renameAttribute("b", "x\\`yz") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula1") - fh.addFormula("Formula1", "`x\\\\\\`yz`+1") - fh.verifyValues("Formula1", [2, 2, 2, 1, 1]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula2") - fh.addFormula("Formula2", "Formula1+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) - fh.renameAttribute("Formula1", "Formula\\`1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("b", "x\\`yz") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula1") + table.addFormula("Formula1", "`x\\\\\\`yz`+1") + table.verifyFormulaValues("Formula1", [2, 2, 2, 1, 1]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula2") + table.addFormula("Formula2", "Formula1+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("Formula1", "Formula\\`1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) }) it("Check single quote symbol in attribute", () => { fh.visitURL("?sample=four") - fh.renameAttribute("b", "x'yz") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula1") - fh.addFormula("Formula1", "`x'yz`+1") - fh.verifyValues("Formula1", [2, 2, 2, 1, 1]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula2") - fh.addFormula("Formula2", "Formula1+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) - fh.renameAttribute("Formula1", "Formula'1") - fh.checkFormulaExists("Formula2", "`Formula'1`+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("b", "x'yz") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula1") + table.addFormula("Formula1", "`x'yz`+1") + table.verifyFormulaValues("Formula1", [2, 2, 2, 1, 1]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula2") + table.addFormula("Formula2", "Formula1+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("Formula1", "Formula'1") + table.checkFormulaExists("Formula2", "`Formula'1`+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) }) it("Check double quotes symbol in attribute", () => { fh.visitURL("?sample=four") - fh.renameAttribute("b", "x\"yz") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula1") - fh.addFormula("Formula1", "`x\"yz`+1") - fh.verifyValues("Formula1", [2, 2, 2, 1, 1]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula2") - fh.addFormula("Formula2", "Formula1+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) - fh.renameAttribute("Formula1", "Formula\"1") - fh.checkFormulaExists("Formula2", "`Formula\"1`+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("b", "x\"yz") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula1") + table.addFormula("Formula1", "`x\"yz`+1") + table.verifyFormulaValues("Formula1", [2, 2, 2, 1, 1]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula2") + table.addFormula("Formula2", "Formula1+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("Formula1", "Formula\"1") + table.checkFormulaExists("Formula2", "`Formula\"1`+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) }) it("Check backslash and double quotes symbols in attribute", () => { fh.visitURL("?sample=four") - fh.renameAttribute("b", "x\\\"yz") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula1") + table.renameAttribute("b", "x\\\"yz") + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula1") // must add {del} to delete auto-matched (in error) closing quote - fh.addFormula("Formula1", "`x\\\"yz`+1{del}") - fh.verifyValues("Formula1", [2, 2, 2, 1, 1]) - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula2") - fh.addFormula("Formula2", "Formula1+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) - fh.renameAttribute("Formula1", "Formula\\\"1") - fh.checkFormulaExists("Formula2", "`Formula\\\\\"1`+1") - fh.verifyValues("Formula2", [3, 3, 3, 2, 2]) + table.addFormula("Formula1", "`x\\\"yz`+1{del}") + table.verifyFormulaValues("Formula1", [2, 2, 2, 1, 1]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula2") + table.addFormula("Formula2", "Formula1+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) + table.renameAttribute("Formula1", "Formula\\\"1") + table.checkFormulaExists("Formula2", "`Formula\\\\\"1`+1") + table.verifyFormulaValues("Formula2", [3, 3, 3, 2, 2]) }) it("Check double quote in string constant", () => { fh.visitURL("?sample=four") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") - fh.addFormula("Formula", "if(b = 1, \"o\\\"k\", \"ok\")") - fh.verifyValues("Formula", ["o\"k", "o\"k", "o\"k", "ok", "ok"]) - fh.editFormula("Formula", "if(b = 1, \"o\\\\k\", \"ok\")") - fh.verifyValues("Formula", ["o\\k", "o\\k", "o\\k", "ok", "ok"]) - fh.editFormula("Formula", "if(b = 1, \"o\\\\\\\"k\", \"ok\")") - fh.verifyValues("Formula", ["o\\\"k", "o\\\"k", "o\\\"k", "ok", "ok"]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") + table.addFormula("Formula", "if(b = 1, \"o\\\"k\", \"ok\")") + table.verifyFormulaValues("Formula", ["o\"k", "o\"k", "o\"k", "ok", "ok"]) + table.editFormula("Formula", "if(b = 1, \"o\\\\k\", \"ok\")") + table.verifyFormulaValues("Formula", ["o\\k", "o\\k", "o\\k", "ok", "ok"]) + table.editFormula("Formula", "if(b = 1, \"o\\\\\\\"k\", \"ok\")") + table.verifyFormulaValues("Formula", ["o\\\"k", "o\\\"k", "o\\\"k", "ok", "ok"]) }) it("Check double quote in symbol name", () => { fh.visitURL("?sample=four") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") - fh.addFormula("Formula", "lookupByIndex(\"Four\", \"b\", caseIndex)") - fh.renameAttribute("b", "x'yz") - fh.checkFormulaExists("Formula", "lookupByIndex(\"Four\", \"x'yz\", caseIndex)") - fh.verifyValues("Formula", [1, 1, 1, "", ""]) - fh.renameAttribute("x'yz", "x\"yz") - fh.checkFormulaExists("Formula", "lookupByIndex(\"Four\", \"x\\\"yz\", caseIndex)") - fh.verifyValues("Formula", [1, 1, 1, "", ""]) - fh.renameAttribute("x\\\"yz", "x\\yz") - fh.checkFormulaExists("Formula", "lookupByIndex(\"Four\", \"x\\\\yz\", caseIndex)") - fh.verifyValues("Formula", [1, 1, 1, "", ""]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") + table.addFormula("Formula", "lookupByIndex(\"Four\", \"b\", caseIndex)") + table.renameAttribute("b", "x'yz") + table.checkFormulaExists("Formula", "lookupByIndex(\"Four\", \"x'yz\", caseIndex)") + table.verifyFormulaValues("Formula", [1, 1, 1, "", ""]) + table.renameAttribute("x'yz", "x\"yz") + table.checkFormulaExists("Formula", "lookupByIndex(\"Four\", \"x\\\"yz\", caseIndex)") + table.verifyFormulaValues("Formula", [1, 1, 1, "", ""]) + table.renameAttribute("x\\\"yz", "x\\yz") + table.checkFormulaExists("Formula", "lookupByIndex(\"Four\", \"x\\\\yz\", caseIndex)") + table.verifyFormulaValues("Formula", [1, 1, 1, "", ""]) }) it("Check single quote in string constant", () => { fh.visitURL("?sample=four") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") - fh.addFormula("Formula", "if(b = 1, 'ok', 'not ok')") - fh.verifyValues("Formula", ["ok", "ok", "ok", "not ok", "not ok"]) - fh.editFormula("Formula", "if(b = 1, 'o\"k', 'not ok')") - fh.verifyValues("Formula", ["o\"k", "o\"k", "o\"k", "not ok", "not ok"]) - fh.editFormula("Formula", "if(b = 1, 'o\\'k', 'not ok')") - fh.verifyValues("Formula", ["o'k", "o'k", "o'k", "not ok", "not ok"]) - fh.editFormula("Formula", "if(b = 1, 'o\\\\k', 'not ok')") - fh.verifyValues("Formula", ["o\\k", "o\\k", "o\\k", "not ok", "not ok"]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") + table.addFormula("Formula", "if(b = 1, 'ok', 'not ok')") + table.verifyFormulaValues("Formula", ["ok", "ok", "ok", "not ok", "not ok"]) + table.editFormula("Formula", "if(b = 1, 'o\"k', 'not ok')") + table.verifyFormulaValues("Formula", ["o\"k", "o\"k", "o\"k", "not ok", "not ok"]) + table.editFormula("Formula", "if(b = 1, 'o\\'k', 'not ok')") + table.verifyFormulaValues("Formula", ["o'k", "o'k", "o'k", "not ok", "not ok"]) + table.editFormula("Formula", "if(b = 1, 'o\\\\k', 'not ok')") + table.verifyFormulaValues("Formula", ["o\\k", "o\\k", "o\\k", "not ok", "not ok"]) }) it("Check single quote in symbol name", () => { fh.visitURL("?sample=four") - fh.addNewAttribute() - fh.renameAttribute("newAttr", "Formula") - fh.addFormula("Formula", "lookupByIndex('Four', 'b', caseIndex)") - fh.renameAttribute("b", "x'yz") - fh.checkFormulaExists("Formula", "lookupByIndex('Four', 'x\\'yz', caseIndex)") - fh.verifyValues("Formula", [1, 1, 1, "", ""]) - fh.renameAttribute("x'yz", "x\"yz") - fh.checkFormulaExists("Formula", "lookupByIndex('Four', 'x\"yz', caseIndex)") - fh.verifyValues("Formula", [1, 1, 1, "", ""]) - fh.renameAttribute("x\\\"yz", "x\\yz") - fh.checkFormulaExists("Formula", "lookupByIndex('Four', 'x\\\\yz', caseIndex)") - fh.verifyValues("Formula", [1, 1, 1, "", ""]) + table.addNewAttribute() + table.renameAttribute("newAttr", "Formula") + table.addFormula("Formula", "lookupByIndex('Four', 'b', caseIndex)") + table.renameAttribute("b", "x'yz") + table.checkFormulaExists("Formula", "lookupByIndex('Four', 'x\\'yz', caseIndex)") + table.verifyFormulaValues("Formula", [1, 1, 1, "", ""]) + table.renameAttribute("x'yz", "x\"yz") + table.checkFormulaExists("Formula", "lookupByIndex('Four', 'x\"yz', caseIndex)") + table.verifyFormulaValues("Formula", [1, 1, 1, "", ""]) + table.renameAttribute("x\\\"yz", "x\\yz") + table.checkFormulaExists("Formula", "lookupByIndex('Four', 'x\\\\yz', caseIndex)") + table.verifyFormulaValues("Formula", [1, 1, 1, "", ""]) }) }) }) diff --git a/v3/cypress/e2e/table.spec.ts b/v3/cypress/e2e/table.spec.ts index b34171c49..a305a3317 100644 --- a/v3/cypress/e2e/table.spec.ts +++ b/v3/cypress/e2e/table.spec.ts @@ -1,7 +1,6 @@ import { TableTileElements as table } from "../support/elements/table-tile" import { ComponentElements as c } from "../support/elements/component-elements" import { ToolbarElements as toolbar } from "../support/elements/toolbar-elements" -import { FormulaHelper as fh } from "../support/helpers/formula-helper" import { ColorPickerPaletteElements as cpp } from "../support/elements/color-picker-palette" context("case table ui", () => { @@ -417,7 +416,7 @@ context("case table ui", () => { describe("case table header attribute menu", () => { it("verify add attribute with undo and redo", ()=>{ // Add new attribute using Add New Attribute button (+) - fh.addNewAttribute() + table.addNewAttribute() // verify new attribute exists table.getColumnHeaders().should("have.length.be.within", 10, 11) diff --git a/v3/cypress/support/elements/slider-tile.ts b/v3/cypress/support/elements/slider-tile.ts index b18f9ab84..08301322e 100644 --- a/v3/cypress/support/elements/slider-tile.ts +++ b/v3/cypress/support/elements/slider-tile.ts @@ -126,5 +126,11 @@ export const SliderTileElements = { // Choose either Numeric or Date-Time based on the provided scaleType cy.clickWhenClickable(`[data-testid="slider-scale-${scaleType}"]`) // cy.get('[data-testid="slider-date-display"]').should("have.text", today) - } + }, + addSlider() { + c.getIconFromToolShelf("slider").click() + }, + deleteSlider() { + c.closeComponent("slider") + }, } diff --git a/v3/cypress/support/elements/table-tile.ts b/v3/cypress/support/elements/table-tile.ts index d0b4fbd49..7f4471038 100644 --- a/v3/cypress/support/elements/table-tile.ts +++ b/v3/cypress/support/elements/table-tile.ts @@ -1,4 +1,6 @@ import { ComponentElements as c } from "./component-elements" +import { FormulaHelper as fh } from "../helpers/formula-helper" + type TestAttributes = Array<{ name: string, move: string }> type TestValues = Record @@ -415,7 +417,8 @@ export const TableTileElements = { addFilterFormulaInModal(formula: string) { this.getHideShowButton().click() this.getHideShowMenuItem(/(Add|Edit) Filter Formula.../).click() - cy.get(".codap-modal-content [data-testid=attr-formula-input]").type(`{selectAll}{del}${formula}`) + fh.clearFormulaInput() + fh.addFilterFormula(formula) cy.get(".codap-modal-content [data-testid=Apply-button]").should("be.visible").click() cy.get("[data-testid=Apply-button]").click() }, diff --git a/v3/cypress/support/helpers/formula-helper.ts b/v3/cypress/support/helpers/formula-helper.ts index 5b9c64ea5..8f356ab04 100644 --- a/v3/cypress/support/helpers/formula-helper.ts +++ b/v3/cypress/support/helpers/formula-helper.ts @@ -1,6 +1,3 @@ -import { ComponentElements as c } from "../elements/component-elements" -import { TableTileElements as table } from "../elements/table-tile" -import { SliderTileElements as slider } from "../elements/slider-tile" import { CfmElements as cfm } from "../elements/cfm" const isMac = navigator.platform.toLowerCase().includes("mac") @@ -16,46 +13,6 @@ export const FormulaHelper = { cfm.openLocalDoc(file) cy.wait(1000) }, - addNewAttribute(collectionIndex = 1) { - table.addNewAttribute(collectionIndex) - }, - renameAttribute(currentAttributeName: string, newAttributeName: string, collectionIndex = 1) { - table.renameAttribute(currentAttributeName, newAttributeName, collectionIndex) - }, - deleteAttribute(attributeName: string, collectionIndex = 1) { - table.deleteAttribute(attributeName, collectionIndex) - }, - addFormula(attributeName: string, formula: string, collectionIndex = 1) { - table.addFormula(attributeName, formula, collectionIndex) - }, - verifyValues(attributeName: string, values: Array, collectionIndex = 1) { - table.verifyFormulaValues(attributeName, values, collectionIndex) - }, - checkFormulaExists(attributeName: string, formula: string, collectionIndex = 1) { - table.checkFormulaExists(attributeName, formula, collectionIndex) - }, - editFormula(attributeName: string, formula: string, collectionIndex = 1) { - table.editFormula(attributeName, formula, collectionIndex) - }, - createNewDataset() { - table.createNewTableFromToolShelf() - }, - insertCases(rowIndex: number, numOfCases: number) { - table.openIndexMenuForRow(rowIndex) - table.insertCases(numOfCases, "after") - }, - changeSliderVariableName(sliderVariableName: string) { - slider.changeVariableName(sliderVariableName) - }, - changeSliderValue(sliderValue: string) { - slider.changeVariableValue(sliderValue) - }, - addSlider() { - c.getIconFromToolShelf("slider").click() - }, - deleteSlider() { - c.closeComponent("slider") - }, clearFormulaInput() { cy.get("[data-testid=formula-editor-input] .cm-content").should("be.visible").and("have.focus") cy.get("[data-testid=formula-editor-input] .cm-content").realPress([metaCtrlKey, "A"]) diff --git a/v3/src/components/case-tile-common/hide-show-menu-list.tsx b/v3/src/components/case-tile-common/hide-show-menu-list.tsx index ca7714927..1f388530e 100644 --- a/v3/src/components/case-tile-common/hide-show-menu-list.tsx +++ b/v3/src/components/case-tile-common/hide-show-menu-list.tsx @@ -7,8 +7,8 @@ import { useDataSetContext } from "../../hooks/use-data-set-context" import { hideAttributeNotification } from "../../models/data/data-set-notifications" import { addSetAsideCases, restoreSetAsideCases } from "../../models/data/data-set-utils" import { t } from "../../utilities/translation/translate" -import { EditFilterFormulaModal } from "../common/edit-filter-formula-modal" import { IMenuItem, StdMenuList } from "./std-menu-list" +import { EditFormulaModal } from "../common/edit-formula-modal" export const HideShowMenuList = observer(function HideShowMenuList() { const data = useDataSetContext() @@ -104,7 +104,13 @@ export const HideShowMenuList = observer(function HideShowMenuList() { { data && - + } ) diff --git a/v3/src/components/common/edit-filter-formula-modal.tsx b/v3/src/components/common/edit-filter-formula-modal.tsx deleted file mode 100644 index 5f7cb5fe9..000000000 --- a/v3/src/components/common/edit-filter-formula-modal.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { - Button, FormControl, FormLabel, ModalBody, ModalCloseButton, ModalFooter, ModalHeader, - Textarea, Tooltip -} from "@chakra-ui/react" -import { observer } from "mobx-react-lite" -import React, { useEffect, useState } from "react" -import { IFormula } from "../../models/formula/formula" -import { t } from "../../utilities/translation/translate" -import { CodapModal } from "../codap-modal" - -interface IFormulaSource { - filterFormula?: IFormula - filterFormulaError: string - setFilterFormula: (formula: string) => void - applyModelChange: (change: () => void, options: { undoStringKey: string, redoStringKey: string }) => void -} -interface IProps { - formulaSource: IFormulaSource - isOpen: boolean - onClose: () => void -} - -export const EditFilterFormulaModal = observer(function EditFormulaModal({ formulaSource, isOpen, onClose }: IProps) { - const [formula, setFormula] = useState("") - - useEffect(() => { - setFormula(formulaSource.filterFormula?.display || "") - }, [formulaSource.filterFormula?.display]) - - const closeModal = () => { - onClose() - } - - function applyFilterFormula() { - formulaSource.applyModelChange(() => { - formulaSource.setFilterFormula(formula) - }, { - undoStringKey: "V3.Undo.hideShowMenu.changeFilterFormula", - redoStringKey: "V3.Redo.hideShowMenu.changeFilterFormula" - }) - closeModal() - } - - const handleFormulaChange = (e: React.ChangeEvent) => setFormula(e.target.value) - - const buttons = [{ - label: t("DG.AttrFormView.cancelBtnTitle"), - tooltip: t("DG.AttrFormView.cancelBtnTooltip"), - onClick: closeModal - }, { - label: t("DG.AttrFormView.applyBtnTitle"), - onClick: applyFilterFormula, - default: true - }] - - return ( - - -
-
- - - - { - formulaSource.filterFormulaError && -
- {formulaSource.filterFormulaError} -
- } - - {t("DG.AttrFormView.formulaPrompt")} -