diff --git a/v3/cypress/e2e/formula/formula-errors.spec.ts b/v3/cypress/e2e/formula/formula-errors.spec.ts index 07f65e5ad..c1d27b501 100644 --- a/v3/cypress/e2e/formula/formula-errors.spec.ts +++ b/v3/cypress/e2e/formula/formula-errors.spec.ts @@ -3,7 +3,7 @@ import { FormulaHelper as fh } from "../../support/helpers/formula-helper" context("Formula Engine", () => { describe("Errors Formula Tests", () => { it("Check invalid functions", () => { - fh.visitURL("?sample=four&dashboard") + fh.visitURL("?sample=four") fh.addNewAttribute() fh.renameAttribute("newAttr", "Formula") fh.addFormula("Formula", "count(aaa)") @@ -22,7 +22,8 @@ context("Formula Engine", () => { "❌ Undefined function c", "❌ Undefined function c" ]) - fh.editFormula("Formula", "count(a") + // need to add {del} because CodeMirror auto-matches parentheses + fh.editFormula("Formula", "count(a{del}") fh.verifyValues("Formula", [ "❌ Syntax error: 'Parenthesis ) expected (char 8)'", "❌ Syntax error: 'Parenthesis ) expected (char 8)'", diff --git a/v3/cypress/e2e/formula/formula-special-characters.spec.ts b/v3/cypress/e2e/formula/formula-special-characters.spec.ts index b96ff2c24..bbe88a1ee 100644 --- a/v3/cypress/e2e/formula/formula-special-characters.spec.ts +++ b/v3/cypress/e2e/formula/formula-special-characters.spec.ts @@ -81,7 +81,8 @@ context("Formula Engine", () => { fh.renameAttribute("b", "x\\\"yz") fh.addNewAttribute() fh.renameAttribute("newAttr", "Formula1") - fh.addFormula("Formula1", "`x\\\"yz`+1") + // 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") diff --git a/v3/cypress/support/e2e.ts b/v3/cypress/support/e2e.ts index 88b1cd44d..6ac38fe52 100644 --- a/v3/cypress/support/e2e.ts +++ b/v3/cypress/support/e2e.ts @@ -22,6 +22,9 @@ import "./commands" // add code coverage support import "@cypress/code-coverage/support" +// add support for dispatching native events (https://github.com/dmtrKovalenko/cypress-real-events) +import "cypress-real-events" + // https://github.com/quasarframework/quasar/issues/2233#issuecomment-1006506083 Cypress.on("uncaught:exception", err => !err.message.includes("ResizeObserver")) diff --git a/v3/cypress/support/elements/table-tile.ts b/v3/cypress/support/elements/table-tile.ts index 206af0d1a..9649f6465 100644 --- a/v3/cypress/support/elements/table-tile.ts +++ b/v3/cypress/support/elements/table-tile.ts @@ -4,6 +4,9 @@ type TestValues = Record type OptString = string | null | undefined +const isMac = navigator.platform.toLowerCase().includes("mac") +const metaCtrlKey = isMac ? "Meta" : "Control" + export const TableTileElements = { getTableTile(index = 0) { return c.getComponentTile("table", index) @@ -387,19 +390,22 @@ export const TableTileElements = { }, addFormulaInModal(attributeName: string, formula: string) { cy.get("[data-testid=attr-name-input]").invoke("attr", "value").should("eq", attributeName) - cy.get("[data-testid=formula-editor-input]").type(formula, {force:true}) + cy.get("[data-testid=formula-editor-input] .cm-content").should("be.visible").and("have.focus") + cy.get("[data-testid=formula-editor-input] .cm-content").realType(formula) cy.get("[data-testid=Apply-button]").click() cy.get("[data-testid=attr-name-input]").should("not.exist") }, clearFormulaInModal(attributeName: string) { cy.get("[data-testid=attr-name-input]").invoke("attr", "value").should("eq", attributeName) - cy.get("[data-testid=formula-editor-input]").type(`{selectAll}{del}`) + 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"]) + cy.get("[data-testid=formula-editor-input] .cm-content").realType("{del}") cy.get("[data-testid=Apply-button]").click() cy.get("[data-testid=attr-name-input]").should("not.exist") }, checkFormulaInModal(attributeName: string, formula: string) { cy.get("[data-testid=attr-name-input]").invoke("attr", "value").should("eq", attributeName) - cy.get("[data-testid=formula-editor-input]").should("have.text", formula) + cy.get("[data-testid=formula-editor-input] .cm-content").should("have.text", formula) cy.get("[data-testid=Cancel-button]").click() cy.get("[data-testid=attr-name-input]").should("not.exist") }, diff --git a/v3/cypress/tsconfig.json b/v3/cypress/tsconfig.json index b60519740..ca6955e46 100644 --- a/v3/cypress/tsconfig.json +++ b/v3/cypress/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "sourceMap": false, - "types": ["cypress"] + "types": ["cypress", "cypress-real-events"] }, "include": [ "./**/*" diff --git a/v3/package-lock.json b/v3/package-lock.json index 1bc7324fc..76ae7ee84 100644 --- a/v3/package-lock.json +++ b/v3/package-lock.json @@ -91,6 +91,7 @@ "clean-webpack-plugin": "^4.0.0", "css-loader": "^7.1.2", "cypress": "^13.13.3", + "cypress-real-events": "^1.13.0", "dotenv-webpack": "^8.1.0", "eslint": "^8.57.0", "eslint-config-react": "^1.1.7", @@ -10294,6 +10295,15 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-real-events": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/cypress-real-events/-/cypress-real-events-1.13.0.tgz", + "integrity": "sha512-LoejtK+dyZ1jaT8wGT5oASTPfsNV8/ClRp99ruN60oPj8cBJYod80iJDyNwfPAu4GCxTXOhhAv9FO65Hpwt6Hg==", + "dev": true, + "peerDependencies": { + "cypress": "^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x || ^13.x" + } + }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -32724,6 +32734,13 @@ } } }, + "cypress-real-events": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/cypress-real-events/-/cypress-real-events-1.13.0.tgz", + "integrity": "sha512-LoejtK+dyZ1jaT8wGT5oASTPfsNV8/ClRp99ruN60oPj8cBJYod80iJDyNwfPAu4GCxTXOhhAv9FO65Hpwt6Hg==", + "dev": true, + "requires": {} + }, "d3": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", diff --git a/v3/package.json b/v3/package.json index de24db353..a9de5717e 100644 --- a/v3/package.json +++ b/v3/package.json @@ -133,6 +133,7 @@ "clean-webpack-plugin": "^4.0.0", "css-loader": "^7.1.2", "cypress": "^13.13.3", + "cypress-real-events": "^1.13.0", "dotenv-webpack": "^8.1.0", "eslint": "^8.57.0", "eslint-config-react": "^1.1.7", diff --git a/v3/src/components/common/formula-editor.tsx b/v3/src/components/common/formula-editor.tsx index f325055e1..3269245c7 100644 --- a/v3/src/components/common/formula-editor.tsx +++ b/v3/src/components/common/formula-editor.tsx @@ -232,11 +232,17 @@ export function FormulaEditor({ formula, setFormula, options: _options }: IProps const { attributes = true, constants = true, functions = true, globals = true, specials = true } = options || {} const fullOptions: ICompletionOptions = { attributes, constants, functions, globals, specials } view.dispatch({ effects: cmUpdateOptionsEffect.of(fullOptions) }) + + // https://discuss.codemirror.net/t/how-to-autofocus-in-cm6/2966 + const focusTimer = setInterval(() => { + view.focus() + if (view.hasFocus) clearInterval(focusTimer) + }, 100) }, [dataSet, options]) const handleFormulaChange = (value: string, viewUpdate: ViewUpdate) => setFormula(value) - return }