diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..e6062e0 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,50 @@ +version: 2.1 + +orbs: + cypress: cypress-io/cypress@2 + +commands: + setup: + steps: + - checkout + - run: + name: write npmrc + command: | + echo "@kingsrook:registry=https://npm.pkg.github.com/" >> .npmrc + echo "NPM_TOKEN=${NPM_TOKEN}" >> .npmrc + echo "@OWNER:registry=https://npm.pkg.github.com/" >> .npmrc + echo "//npm.pkg.github.com/:_authToken=${NPM_TOKEN}" >> .npmrc + - run: + name: copy .env + command: | + cp .circleci/env ./.env + cat ./.env + +jobs: + setup-job: + executor: cypress/base-10 + steps: + - setup + +workflows: + build: + jobs: + - setup-job: + context: [ qqq-maven-registry-credentials ] + - cypress/install: + context: [ qqq-maven-registry-credentials ] + requires: + - setup-job + build: 'cp .circleci/env ./.env && npm run build' # run a custom app build step + - cypress/run: + context: [ qqq-maven-registry-credentials ] + requires: + - cypress/install + start: 'cp .circleci/env ./.env && npm run start' # start server before running tests + wait-on: 'https://localhost:3000' + ## record: true # record results on Cypress Dashboard + ## parallel: true # split all specs across machines + ## parallelism: 4 # use 4 CircleCI machines to finish quickly + ## group: 'all tests' # name this group "all tests" on the dashboard + + diff --git a/.circleci/env b/.circleci/env new file mode 100644 index 0000000..3581308 --- /dev/null +++ b/.circleci/env @@ -0,0 +1,7 @@ +GENERATE_SOURCEMAP=false +HTTPS=true + +REACT_APP_MATERIAL_UI_LICENSE_KEY=${MATERIAL_UI_LICENSE_KEY} + +REACT_APP_GOOGLE_APP_API_KEY=${GOOGLE_APP_API_KEY} +REACT_APP_GOOGLE_APP_CLIENT_ID=${GOOGLE_APP_CLIENT_ID diff --git a/cypress/.gitignore b/cypress/.gitignore new file mode 100644 index 0000000..4094344 --- /dev/null +++ b/cypress/.gitignore @@ -0,0 +1 @@ +videos diff --git a/cypress/e2e/entity-list.spec.cy.ts b/cypress/e2e/entity-list.spec.cy.ts new file mode 100644 index 0000000..886f20c --- /dev/null +++ b/cypress/e2e/entity-list.spec.cy.ts @@ -0,0 +1,83 @@ +/// + +describe("table query screen", () => +{ + + before(() => + { + cy.intercept("GET", "/metaData/authentication", {fixture: "metaData/authentication.json"}).as("authenticationMetaData"); + cy.intercept("GET", "/metaData", {fixture: "metaData/index.json"}).as("metaData"); + cy.intercept("GET", "/metaData/table/person", {fixture: "metaData/table/person.json"}).as("personMetaData"); + cy.intercept("POST", "/data/person/query?*", {fixture: "data/person/index.json"}).as("personQuery"); + cy.intercept("POST", "/data/person/count", {fixture: "data/person/count.json"}).as("personCount"); + cy.intercept("POST", "/data/city/count", {fixture: "data/city/count.json"}).as("cityCount"); + + cy.intercept("GET", "/metaData/process/person.bulkEdit", {fixture: "metaData/process/person.bulkEdit.json"}).as("personBulkEditMetaData"); + cy.intercept("POST", "/processes/person.bulkEdit/init?recordsParam=recordIds&recordIds=1,2,3,4,5", {fixture: "processes/person.bulkEdit/init.json"}).as("personBulkEditInit"); + cy.intercept("POST", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/step/edit", {fixture: "processes/person.bulkEdit/step/edit.json"}).as("personBulkEditStepEdit"); + cy.intercept("GET", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/records?skip=0&limit=10", {fixture: "processes/person.bulkEdit/records.json"}).as("personBulkEditRecords"); + cy.intercept("GET", "/widget/* ", {fixture: "widget/empty.json"}).as("emptyWidget"); + }); + + it("can be loaded from app screen", () => + { + cy.visit("https://localhost:3000/peopleApp/greetingsApp/"); + cy.contains("Person").click(); + cy.location().should((loc) => + { + expect(loc.pathname).to.eq("/peopleApp/greetingsApp/person"); + }); + }); + + it.only("can add query filters", () => + { + ///////////////////////////////////////////////////////////// + // go to table, wait for filter to run, and rows to appear // + ///////////////////////////////////////////////////////////// + cy.visit("https://localhost:3000/peopleApp/greetingsApp/person"); + cy.wait(["@personQuery", "@personCount"]); + cy.get(".MuiDataGrid-virtualScrollerRenderZone").children().should("have.length.greaterThan", 3); + + ///////////////////////////////////////////////////////////////////// + // open the filter window, enter a value, wait for query to re-run // + ///////////////////////////////////////////////////////////////////// + cy.contains("Filters").click(); + cy.get(".MuiDataGrid-filterForm input.MuiInput-input").should("be.focused").type("1"); + + /////////////////////////////////////////////////////////////////// + // assert that query & count both have the expected filter value // + /////////////////////////////////////////////////////////////////// + let expectedFilterContents = JSON.stringify({fieldName: "id", operator: "EQUALS", values: ["1"]}); + cy.wait("@personQuery").its("request.body").should((body) => expect(body).to.contain(expectedFilterContents)); + cy.wait("@personCount").its("request.body").should((body) => expect(body).to.contain(expectedFilterContents)); + + /////////////////////////////////////// + // click away from the filter window // + /////////////////////////////////////// + cy.get("#root").click("topLeft", {force: true}); + cy.contains(".MuiBadge-root", "1").should("be.visible"); + + /////////////////////////////////////////////////////////////////// + // click the 'x' clear icon, then yes, then expect another query // + /////////////////////////////////////////////////////////////////// + cy.waitForStableDOM(); + cy.get("#clearFiltersButton").should("be.visible").click(); + cy.contains("button", "Yes").click(); + + //////////////////////////////////////////////////////////////////// + // assert that query & count both no longer have the filter value // + //////////////////////////////////////////////////////////////////// + cy.wait("@personQuery").its("request.body").should((body) => expect(body).not.to.contain(expectedFilterContents)); + cy.wait("@personCount").its("request.body").should((body) => expect(body).not.to.contain(expectedFilterContents)); + cy.contains(".MuiDataGrid-toolbarContainer .MuiBadge-root", "1").should("not.exist"); + }); + + // tests to add: + // - filter boolean OR + // - sort column + // - all field types and operators + // - pagination, page size + // - check marks, select all + // - column chooser + +}); diff --git a/cypress/e2e/spec.cy.ts b/cypress/e2e/spec.cy.ts deleted file mode 100644 index ac0fd02..0000000 --- a/cypress/e2e/spec.cy.ts +++ /dev/null @@ -1,72 +0,0 @@ -// noinspection ES6UnusedImports -import * as cypress from "cypress"; - -describe("empty spec", () => -{ - it("passes", () => - { - cy.intercept("GET", "/metaData", {fixture: "metaData/index.json"}).as("metaData"); - cy.intercept("GET", "/data/person?*", {fixture: "data/person/index.json"}).as("personQuery"); - cy.intercept("GET", "/data/person/count?*", {fixture: "data/person/count.json"}).as("personCount") - cy.intercept("GET", "/metaData/process/person.bulkEdit", {fixture: "metaData/process/person.bulkEdit.json"}).as("personBulkEditMetaData") - cy.intercept("POST", "/processes/person.bulkEdit/init?recordsParam=recordIds&recordIds=1,2,3,4,5", {fixture: "processes/person.bulkEdit/init.json"}).as("personBulkEditInit") - cy.intercept("POST", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/step/edit", {fixture: "processes/person.bulkEdit/step/edit.json"}).as("personBulkEditStepEdit") - cy.intercept("GET", "/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/records?skip=0&limit=10", {fixture: "processes/person.bulkEdit/records.json"}).as("personBulkEditRecords") - - ///////////////// - // home screen // - ///////////////// - cy.visit("http://localhost:3000/"); - cy.wait(["@metaData"]) - - cy.contains(".MuiListItem-root", "Tables").click(); - cy.contains(".MuiListItem-root", "Person").click(); - - ///////////////////////// - // person query screen // - ///////////////////////// - cy.location().should((loc) => - { - expect(loc.pathname).to.eq("/person") - }); - cy.wait(["@personQuery", "@personCount"]) - - cy.get(".MuiDataGrid-columnHeaders input[type='checkbox']").click(); - cy.contains("button", "Bulk Actions").click(); - cy.contains("li", "Bulk Edit").click(); - - //////////////////////////// - // bulk edit process init // - //////////////////////////// - cy.location().should((loc) => - { - expect(loc.pathname).to.eq("/processes/person.bulkEdit"); - expect(loc.search).to.eq("?recordsParam=recordIds&recordIds=1,2,3,4,5"); - }); - cy.wait(["@personBulkEditMetaData"]) - cy.wait(["@personBulkEditInit"]) - - cy.contains("p[variation='h5']", "Edit Values"); - cy.get("#bulkEditSwitch-firstName").click(); - cy.get("input[name='firstName']").click() - .type("Kahhhhn"); - cy.contains("button", "next").click(); - - /////////////////////////// - // bulk edit review step // - /////////////////////////// - cy.contains("p[variation='h5']", "Review"); - cy.contains(".MuiDataGrid-cellContent", "Kahhhhn"); - - cy.contains("button", "submit").click(); - cy.wait(["@personBulkEditStepEdit"]) - cy.wait(["@personBulkEditRecords"]) - - //////////////////////////// - // bulk edit result step // - //////////////////////////// - cy.contains("p[variation='h5']", "Results"); - cy.wait(["@personBulkEditRecords"]) - }); - -}); diff --git a/cypress/fixtures/data/city/count.json b/cypress/fixtures/data/city/count.json new file mode 100644 index 0000000..92fba2e --- /dev/null +++ b/cypress/fixtures/data/city/count.json @@ -0,0 +1,3 @@ +{ + "count": 101406 +} diff --git a/cypress/fixtures/data/person/count.json b/cypress/fixtures/data/person/count.json index 8d1d5b4..92fba2e 100644 --- a/cypress/fixtures/data/person/count.json +++ b/cypress/fixtures/data/person/count.json @@ -1,3 +1,3 @@ { - "count": 5 + "count": 101406 } diff --git a/cypress/fixtures/metaData/authentication.json b/cypress/fixtures/metaData/authentication.json new file mode 100644 index 0000000..71c87b5 --- /dev/null +++ b/cypress/fixtures/metaData/authentication.json @@ -0,0 +1,4 @@ +{ + "name": "mock", + "type": "MOCK" +} diff --git a/cypress/fixtures/metaData/index.json b/cypress/fixtures/metaData/index.json index 6858e0b..0f8133d 100644 --- a/cypress/fixtures/metaData/index.json +++ b/cypress/fixtures/metaData/index.json @@ -1,102 +1,550 @@ { - "tables": { - "carrier": { - "name": "carrier", - "label": "Carrier", - "isHidden": false - }, - "city": { - "name": "city", - "label": "Cities", - "isHidden": true - }, - "person": { - "name": "person", - "label": "Person", - "isHidden": false - } - }, - "processes": { - "person.bulkInsert": { - "name": "person.bulkInsert", - "label": "Person Bulk Insert", - "tableName": "person", - "isHidden": true - }, - "carrier.bulkInsert": { - "name": "carrier.bulkInsert", - "label": "Carrier Bulk Insert", - "tableName": "carrier", - "isHidden": true - }, - "simpleSleep": { - "name": "simpleSleep", - "label": "Simple Sleep", - "isHidden": true - }, - "carrier.bulkDelete": { - "name": "carrier.bulkDelete", - "label": "Carrier Bulk Delete", - "tableName": "carrier", - "isHidden": true - }, - "greet": { - "name": "greet", - "label": "Greet People", - "tableName": "person", - "isHidden": true - }, - "city.bulkDelete": { - "name": "city.bulkDelete", - "label": "Cities Bulk Delete", - "tableName": "city", - "isHidden": true - }, - "person.bulkDelete": { - "name": "person.bulkDelete", - "label": "Person Bulk Delete", - "tableName": "person", - "isHidden": true - }, - "carrier.bulkEdit": { - "name": "carrier.bulkEdit", - "label": "Carrier Bulk Edit", - "tableName": "carrier", - "isHidden": true - }, - "person.bulkEdit": { - "name": "person.bulkEdit", - "label": "Person Bulk Edit", - "tableName": "person", - "isHidden": true - }, - "greetInteractive": { - "name": "greetInteractive", - "label": "Greet Interactive", - "tableName": "person", - "isHidden": false - }, - "city.bulkEdit": { - "name": "city.bulkEdit", - "label": "Cities Bulk Edit", - "tableName": "city", - "isHidden": true - }, - "simpleThrow": { - "name": "simpleThrow", - "label": "Simple Throw", - "isHidden": false - }, - "sleepInteractive": { - "name": "sleepInteractive", - "label": "Sleep Interactive", - "isHidden": false - }, - "city.bulkInsert": { - "name": "city.bulkInsert", - "label": "Cities Bulk Insert", - "tableName": "city", - "isHidden": true - } - } + "tables": { + "carrier": { + "name": "carrier", + "label": "Carrier", + "isHidden": false, + "iconName": "local_shipping", + "capabilities": [ + "TABLE_COUNT", + "TABLE_GET", + "TABLE_QUERY", + "TABLE_UPDATE", + "TABLE_INSERT", + "TABLE_DELETE" + ] + }, + "person": { + "name": "person", + "label": "Person", + "isHidden": false, + "iconName": "person", + "capabilities": [ + "TABLE_COUNT", + "TABLE_GET", + "TABLE_QUERY", + "TABLE_UPDATE", + "TABLE_INSERT", + "TABLE_DELETE" + ] + }, + "city": { + "name": "city", + "label": "Cities", + "isHidden": true, + "iconName": "location_city", + "capabilities": [ + "TABLE_COUNT", + "TABLE_GET", + "TABLE_QUERY", + "TABLE_UPDATE", + "TABLE_INSERT", + "TABLE_DELETE" + ] + } + }, + "processes": { + "greet": { + "name": "greet", + "label": "Greet People", + "tableName": "person", + "isHidden": true, + "iconName": "emoji_people" + }, + "greetInteractive": { + "name": "greetInteractive", + "label": "Greet Interactive", + "tableName": "person", + "isHidden": false, + "iconName": "waving_hand" + }, + "clonePeople": { + "name": "clonePeople", + "label": "Clone People", + "tableName": "person", + "isHidden": false, + "iconName": "content_copy" + }, + "simpleSleep": { + "name": "simpleSleep", + "label": "Simple Sleep", + "isHidden": true + }, + "sleepInteractive": { + "name": "sleepInteractive", + "label": "Sleep Interactive", + "isHidden": false + }, + "simpleThrow": { + "name": "simpleThrow", + "label": "Simple Throw", + "isHidden": false + }, + "carrier.bulkInsert": { + "name": "carrier.bulkInsert", + "label": "Carrier Bulk Insert", + "tableName": "carrier", + "isHidden": true + }, + "carrier.bulkEdit": { + "name": "carrier.bulkEdit", + "label": "Carrier Bulk Edit", + "tableName": "carrier", + "isHidden": true + }, + "carrier.bulkDelete": { + "name": "carrier.bulkDelete", + "label": "Carrier Bulk Delete", + "tableName": "carrier", + "isHidden": true + }, + "person.bulkInsert": { + "name": "person.bulkInsert", + "label": "Person Bulk Insert", + "tableName": "person", + "isHidden": true + }, + "person.bulkEdit": { + "name": "person.bulkEdit", + "label": "Person Bulk Edit", + "tableName": "person", + "isHidden": true + }, + "person.bulkDelete": { + "name": "person.bulkDelete", + "label": "Person Bulk Delete", + "tableName": "person", + "isHidden": true + }, + "city.bulkInsert": { + "name": "city.bulkInsert", + "label": "Cities Bulk Insert", + "tableName": "city", + "isHidden": true + }, + "city.bulkEdit": { + "name": "city.bulkEdit", + "label": "Cities Bulk Edit", + "tableName": "city", + "isHidden": true + }, + "city.bulkDelete": { + "name": "city.bulkDelete", + "label": "Cities Bulk Delete", + "tableName": "city", + "isHidden": true + } + }, + "apps": { + "greetingsApp": { + "name": "greetingsApp", + "label": "Greetings App", + "iconName": "emoji_people", + "widgets": [ + "PersonsByCreateDateBarChart", + "QuickSightChartRenderer" + ], + "children": [ + { + "type": "PROCESS", + "name": "greet", + "label": "Greet People", + "iconName": "emoji_people" + }, + { + "type": "TABLE", + "name": "person", + "label": "Person", + "iconName": "person" + }, + { + "type": "TABLE", + "name": "city", + "label": "Cities", + "iconName": "location_city" + }, + { + "type": "PROCESS", + "name": "greetInteractive", + "label": "Greet Interactive", + "iconName": "waving_hand" + } + ], + "childMap": { + "greetInteractive": { + "type": "PROCESS", + "name": "greetInteractive", + "label": "Greet Interactive", + "iconName": "waving_hand" + }, + "city": { + "type": "TABLE", + "name": "city", + "label": "Cities", + "iconName": "location_city" + }, + "person": { + "type": "TABLE", + "name": "person", + "label": "Person", + "iconName": "person" + }, + "greet": { + "type": "PROCESS", + "name": "greet", + "label": "Greet People", + "iconName": "emoji_people" + } + }, + "sections": [ + { + "name": "greetingsApp", + "label": "Greetings App", + "icon": { + "name": "badge" + }, + "tables": [ + "person", + "city" + ], + "processes": [ + "greet", + "greetInteractive" + ] + } + ] + }, + "peopleApp": { + "name": "peopleApp", + "label": "People App", + "iconName": "person", + "widgets": [], + "children": [ + { + "type": "APP", + "name": "greetingsApp", + "label": "Greetings App", + "iconName": "emoji_people" + }, + { + "type": "PROCESS", + "name": "clonePeople", + "label": "Clone People", + "iconName": "content_copy" + } + ], + "childMap": { + "greetingsApp": { + "type": "APP", + "name": "greetingsApp", + "label": "Greetings App", + "iconName": "emoji_people" + }, + "clonePeople": { + "type": "PROCESS", + "name": "clonePeople", + "label": "Clone People", + "iconName": "content_copy" + } + }, + "sections": [ + { + "name": "peopleApp", + "label": "People App", + "icon": { + "name": "badge" + }, + "processes": [ + "clonePeople" + ] + } + ] + }, + "miscellaneous": { + "name": "miscellaneous", + "label": "Miscellaneous", + "iconName": "stars", + "widgets": [], + "children": [ + { + "type": "TABLE", + "name": "carrier", + "label": "Carrier", + "iconName": "local_shipping" + }, + { + "type": "PROCESS", + "name": "simpleSleep", + "label": "Simple Sleep" + }, + { + "type": "PROCESS", + "name": "sleepInteractive", + "label": "Sleep Interactive" + }, + { + "type": "PROCESS", + "name": "simpleThrow", + "label": "Simple Throw" + } + ], + "childMap": { + "carrier": { + "type": "TABLE", + "name": "carrier", + "label": "Carrier", + "iconName": "local_shipping" + }, + "simpleSleep": { + "type": "PROCESS", + "name": "simpleSleep", + "label": "Simple Sleep" + }, + "simpleThrow": { + "type": "PROCESS", + "name": "simpleThrow", + "label": "Simple Throw" + }, + "sleepInteractive": { + "type": "PROCESS", + "name": "sleepInteractive", + "label": "Sleep Interactive" + } + }, + "sections": [ + { + "name": "miscellaneous", + "label": "Miscellaneous", + "icon": { + "name": "badge" + }, + "tables": [ + "carrier" + ], + "processes": [ + "simpleSleep", + "sleepInteractive", + "simpleThrow" + ] + } + ] + } + }, + "appTree": [ + { + "type": "APP", + "name": "peopleApp", + "label": "People App", + "children": [ + { + "type": "APP", + "name": "greetingsApp", + "label": "Greetings App", + "children": [ + { + "type": "PROCESS", + "name": "greet", + "label": "Greet People", + "iconName": "emoji_people" + }, + { + "type": "TABLE", + "name": "person", + "label": "Person", + "iconName": "person" + }, + { + "type": "TABLE", + "name": "city", + "label": "Cities", + "iconName": "location_city" + }, + { + "type": "PROCESS", + "name": "greetInteractive", + "label": "Greet Interactive", + "iconName": "waving_hand" + } + ], + "iconName": "emoji_people" + }, + { + "type": "PROCESS", + "name": "clonePeople", + "label": "Clone People", + "iconName": "content_copy" + } + ], + "iconName": "person" + }, + { + "type": "APP", + "name": "miscellaneous", + "label": "Miscellaneous", + "children": [ + { + "type": "TABLE", + "name": "carrier", + "label": "Carrier", + "iconName": "local_shipping" + }, + { + "type": "PROCESS", + "name": "simpleSleep", + "label": "Simple Sleep" + }, + { + "type": "PROCESS", + "name": "sleepInteractive", + "label": "Sleep Interactive" + }, + { + "type": "PROCESS", + "name": "simpleThrow", + "label": "Simple Throw" + } + ], + "iconName": "stars" + } + ], + "branding": { + "logo": "/kr-logo.png", + "icon": "/kr-icon.png" + }, + "widgets": { + "parcelTrackingDetails": { + "name": "parcelTrackingDetails", + "label": "Tracking Details", + "type": "childRecordList" + }, + "deposcoSalesOrderLineItems": { + "name": "deposcoSalesOrderLineItems", + "label": "Line Items", + "type": "childRecordList" + }, + "TotalShipmentsByDayBarChart": { + "name": "TotalShipmentsByDayBarChart", + "label": "Total Shipments By Day", + "type": "chart" + }, + "TotalShipmentsByMonthLineChart": { + "name": "TotalShipmentsByMonthLineChart", + "label": "Total Shipments By Month", + "type": "chart" + }, + "YTDShipmentsByCarrierPieChart": { + "name": "YTDShipmentsByCarrierPieChart", + "label": "Shipments By Carrier Year To Date", + "type": "chart" + }, + "TodaysShipmentsStatisticsCard": { + "name": "TodaysShipmentsStatisticsCard", + "label": "Today's Shipments", + "type": "statistics" + }, + "ShipmentsInTransitStatisticsCard": { + "name": "ShipmentsInTransitStatisticsCard", + "label": "Shipments In Transit", + "type": "statistics" + }, + "OpenOrdersStatisticsCard": { + "name": "OpenOrdersStatisticsCard", + "label": "Open Orders", + "type": "statistics" + }, + "ShippingExceptionsStatisticsCard": { + "name": "ShippingExceptionsStatisticsCard", + "label": "Shipping Exceptions", + "type": "statistics" + }, + "WarehouseLocationCards": { + "name": "WarehouseLocationCards", + "type": "location" + }, + "TotalShipmentsStatisticsCard": { + "name": "TotalShipmentsStatisticsCard", + "label": "Total Shipments", + "type": "statistics" + }, + "SuccessfulDeliveriesStatisticsCard": { + "name": "SuccessfulDeliveriesStatisticsCard", + "label": "Successful Deliveries", + "type": "statistics" + }, + "ServiceFailuresStatisticsCard": { + "name": "ServiceFailuresStatisticsCard", + "label": "Service Failures", + "type": "statistics" + }, + "CarrierVolumeLineChart": { + "name": "CarrierVolumeLineChart", + "label": "Carrier Volume By Month", + "type": "lineChart" + }, + "YTDSpendByCarrierTable": { + "name": "YTDSpendByCarrierTable", + "label": "Spend By Carrier Year To Date", + "type": "table" + }, + "TimeInTransitBarChart": { + "name": "TimeInTransitBarChart", + "label": "Time In Transit Last 30 Days", + "type": "chart" + }, + "OpenBillingWorksheetsTable": { + "name": "OpenBillingWorksheetsTable", + "label": "Open Billing Worksheets", + "type": "table" + }, + "AssociatedParcelInvoicesTable": { + "name": "AssociatedParcelInvoicesTable", + "label": "Associated Parcel Invoices", + "type": "table", + "icon": "receipt" + }, + "BillingWorksheetLinesTable": { + "name": "BillingWorksheetLinesTable", + "label": "Billing Worksheet Lines", + "type": "table" + }, + "RatingIssuesWidget": { + "name": "RatingIssuesWidget", + "label": "Rating Issues", + "type": "html", + "icon": "warning", + "gridColumns": 6 + }, + "UnassignedParcelInvoicesTable": { + "name": "UnassignedParcelInvoicesTable", + "label": "Unassigned Parcel Invoices", + "type": "table" + }, + "ParcelInvoiceSummaryWidget": { + "name": "ParcelInvoiceSummaryWidget", + "label": "Parcel Invoice Summary", + "type": "multiStatistics" + }, + "ParcelInvoiceLineExceptionsSummaryWidget": { + "name": "ParcelInvoiceLineExceptionsSummaryWidget", + "label": "Parcel Invoice Line Exceptions", + "type": "multiStatistics" + }, + "BillingWorksheetStatusStepper": { + "name": "BillingWorksheetStatusStepper", + "label": "Billing Worksheet Progress", + "type": "stepper", + "icon": "refresh", + "gridColumns": 6 + }, + "PersonsByCreateDateBarChart": { + "name": "PersonsByCreateDateBarChart", + "label": "Persons By Create Date", + "type": "barChart" + }, + "QuickSightChartRenderer": { + "name": "QuickSightChartRenderer", + "label": "Quick Sight", + "type": "quickSightChart" + } + } } diff --git a/cypress/fixtures/metaData/process/person.bulkEdit.json b/cypress/fixtures/metaData/process/person.bulkEdit.json index 62b2260..f7b0546 100644 --- a/cypress/fixtures/metaData/process/person.bulkEdit.json +++ b/cypress/fixtures/metaData/process/person.bulkEdit.json @@ -27,7 +27,8 @@ "backendName": "first_name", "type": "STRING", "isRequired": true, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" }, { "name": "lastName", @@ -35,7 +36,8 @@ "backendName": "last_name", "type": "STRING", "isRequired": true, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" }, { "name": "birthDate", @@ -43,14 +45,44 @@ "backendName": "birth_date", "type": "DATE", "isRequired": false, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" }, { "name": "email", "label": "Email", + "backendName": "email", "type": "STRING", "isRequired": false, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" + }, + { + "name": "isEmployed", + "label": "Is Employed", + "backendName": "is_employed", + "type": "BOOLEAN", + "isRequired": false, + "isEditable": true, + "displayFormat": "%s" + }, + { + "name": "annualSalary", + "label": "Annual Salary", + "backendName": "annual_salary", + "type": "DECIMAL", + "isRequired": false, + "isEditable": true, + "displayFormat": "$%,.2f" + }, + { + "name": "daysWorked", + "label": "Days Worked", + "backendName": "days_worked", + "type": "INTEGER", + "isRequired": false, + "isEditable": true, + "displayFormat": "%,d" } ] }, @@ -60,19 +92,7 @@ "stepType": "frontend", "components": [ { - "type": "HELP_TEXT", - "values": { - "text": "The records below will be updated if you click Submit." - } - } - ], - "viewFields": [ - { - "name": "valuesBeingUpdated", - "label": "Values Being Updated", - "type": "STRING", - "isRequired": false, - "isEditable": true + "type": "VALIDATION_REVIEW_SCREEN" } ], "recordListFields": [ @@ -82,7 +102,8 @@ "backendName": "first_name", "type": "STRING", "isRequired": true, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" }, { "name": "lastName", @@ -90,7 +111,8 @@ "backendName": "last_name", "type": "STRING", "isRequired": true, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" }, { "name": "birthDate", @@ -98,83 +120,54 @@ "backendName": "birth_date", "type": "DATE", "isRequired": false, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" }, { "name": "email", "label": "Email", + "backendName": "email", "type": "STRING", "isRequired": false, - "isEditable": true + "isEditable": true, + "displayFormat": "%s" + }, + { + "name": "isEmployed", + "label": "Is Employed", + "backendName": "is_employed", + "type": "BOOLEAN", + "isRequired": false, + "isEditable": true, + "displayFormat": "%s" + }, + { + "name": "annualSalary", + "label": "Annual Salary", + "backendName": "annual_salary", + "type": "DECIMAL", + "isRequired": false, + "isEditable": true, + "displayFormat": "$%,.2f" + }, + { + "name": "daysWorked", + "label": "Days Worked", + "backendName": "days_worked", + "type": "INTEGER", + "isRequired": false, + "isEditable": true, + "displayFormat": "%,d" } ] }, { - "name": "results", - "label": "Results", + "name": "result", + "label": "Result", "stepType": "frontend", "components": [ { - "type": "HELP_TEXT", - "values": { - "text": "The records below have been updated." - } - } - ], - "recordListFields": [ - { - "name": "id", - "label": "Id", - "type": "INTEGER", - "isRequired": false, - "isEditable": false - }, - { - "name": "createDate", - "label": "Create Date", - "backendName": "create_date", - "type": "DATE_TIME", - "isRequired": false, - "isEditable": false - }, - { - "name": "modifyDate", - "label": "Modify Date", - "backendName": "modify_date", - "type": "DATE_TIME", - "isRequired": false, - "isEditable": false - }, - { - "name": "firstName", - "label": "First Name", - "backendName": "first_name", - "type": "STRING", - "isRequired": true, - "isEditable": true - }, - { - "name": "lastName", - "label": "Last Name", - "backendName": "last_name", - "type": "STRING", - "isRequired": true, - "isEditable": true - }, - { - "name": "birthDate", - "label": "Birth Date", - "backendName": "birth_date", - "type": "DATE", - "isRequired": false, - "isEditable": true - }, - { - "name": "email", - "label": "Email", - "type": "STRING", - "isRequired": false, - "isEditable": true + "type": "PROCESS_SUMMARY_RESULTS" } ] } diff --git a/cypress/fixtures/metaData/table/person.json b/cypress/fixtures/metaData/table/person.json new file mode 100644 index 0000000..5f69a3b --- /dev/null +++ b/cypress/fixtures/metaData/table/person.json @@ -0,0 +1,155 @@ +{ + "table": { + "name": "person", + "label": "Person", + "isHidden": false, + "primaryKeyField": "id", + "iconName": "person", + "fields": { + "firstName": { + "name": "firstName", + "label": "First Name", + "type": "STRING", + "isRequired": true, + "isEditable": true, + "displayFormat": "%s" + }, + "lastName": { + "name": "lastName", + "label": "Last Name", + "type": "STRING", + "isRequired": true, + "isEditable": true, + "displayFormat": "%s" + }, + "annualSalary": { + "name": "annualSalary", + "label": "Annual Salary", + "type": "DECIMAL", + "isRequired": false, + "isEditable": true, + "displayFormat": "$%,.2f" + }, + "modifyDate": { + "name": "modifyDate", + "label": "Modify Date", + "type": "DATE_TIME", + "isRequired": false, + "isEditable": false, + "displayFormat": "%s" + }, + "daysWorked": { + "name": "daysWorked", + "label": "Days Worked", + "type": "INTEGER", + "isRequired": false, + "isEditable": true, + "displayFormat": "%,d" + }, + "id": { + "name": "id", + "label": "Id", + "type": "INTEGER", + "isRequired": false, + "isEditable": false, + "displayFormat": "%s" + }, + "birthDate": { + "name": "birthDate", + "label": "Birth Date", + "type": "DATE", + "isRequired": false, + "isEditable": true, + "displayFormat": "%s" + }, + "isEmployed": { + "name": "isEmployed", + "label": "Is Employed", + "type": "BOOLEAN", + "isRequired": false, + "isEditable": true, + "displayFormat": "%s" + }, + "email": { + "name": "email", + "label": "Email", + "type": "STRING", + "isRequired": false, + "isEditable": true, + "displayFormat": "%s" + }, + "createDate": { + "name": "createDate", + "label": "Create Date", + "type": "DATE_TIME", + "isRequired": false, + "isEditable": false, + "displayFormat": "%s" + } + }, + "sections": [ + { + "name": "identity", + "label": "Identity", + "tier": "T1", + "fieldNames": [ + "id", + "firstName", + "lastName" + ], + "icon": { + "name": "badge" + }, + "isHidden": false + }, + { + "name": "basicInfo", + "label": "Basic Info", + "tier": "T2", + "fieldNames": [ + "email", + "birthDate" + ], + "icon": { + "name": "dataset" + }, + "isHidden": false + }, + { + "name": "employmentInfo", + "label": "Employment Info", + "tier": "T2", + "fieldNames": [ + "isEmployed", + "annualSalary", + "daysWorked" + ], + "icon": { + "name": "work" + }, + "isHidden": false + }, + { + "name": "dates", + "label": "Dates", + "tier": "T3", + "fieldNames": [ + "createDate", + "modifyDate" + ], + "icon": { + "name": "calendar_month" + }, + "isHidden": false + } + ], + "capabilities": [ + "TABLE_COUNT", + "TABLE_GET", + "TABLE_QUERY", + "TABLE_DELETE", + "TABLE_INSERT", + "TABLE_UPDATE" + ] + } +} \ No newline at end of file diff --git a/cypress/fixtures/processes/person.bulkEdit/records.json b/cypress/fixtures/processes/person.bulkEdit/records.json new file mode 100644 index 0000000..1765643 --- /dev/null +++ b/cypress/fixtures/processes/person.bulkEdit/records.json @@ -0,0 +1,3 @@ +[ + {} +] \ No newline at end of file diff --git a/cypress/fixtures/widget/empty.json b/cypress/fixtures/widget/empty.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/cypress/fixtures/widget/empty.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 698b01a..41cc226 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -34,4 +34,8 @@ // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable // } // } -// } \ No newline at end of file +// } + +import {registerCommand} from "cypress-wait-for-stable-dom"; + +registerCommand({pollInterval: 100, timeout: 3000}); diff --git a/cypress/videos/spec.cy.ts.mp4 b/cypress/videos/spec.cy.ts.mp4 deleted file mode 100644 index 0acb14d..0000000 Binary files a/cypress/videos/spec.cy.ts.mp4 and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 09889cb..e407ac7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@fullcalendar/interaction": "5.10.0", "@fullcalendar/react": "5.10.0", "@fullcalendar/timegrid": "5.10.0", - "@kingsrook/qqq-frontend-core": "1.0.25", + "@kingsrook/qqq-frontend-core": "1.0.36", "@mui/icons-material": "5.4.1", "@mui/material": "5.4.1", "@mui/styled-engine": "5.4.1", @@ -38,6 +38,7 @@ "@types/react": "17.0.38", "@types/react-dom": "17.0.11", "@types/react-router-hash-link": "2.4.5", + "ace-builds": "1.12.3", "chart.js": "3.4.1", "chroma-js": "2.4.2", "datejs": "1.0.0-rc3", @@ -49,6 +50,7 @@ "html-react-parser": "1.4.8", "http-proxy-middleware": "2.0.6", "react": "17.0.2", + "react-ace": "10.1.0", "react-chartjs-2": "3.0.4", "react-cookie": "4.1.1", "react-dom": "17.0.2", @@ -78,7 +80,8 @@ "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.10.2", "@typescript-eslint/parser": "5.10.2", - "cypress": "10.3.1", + "cypress": "11.0.1", + "cypress-wait-for-stable-dom": "0.1.0", "eslint": "8.8.0", "eslint-config-airbnb": "19.0.4", "eslint-import-resolver-typescript": "2.5.0", @@ -86,6 +89,7 @@ "eslint-plugin-jsx-a11y": "6.5.1", "eslint-plugin-react": "7.28.0", "eslint-plugin-react-hooks": "4.3.0", + "start-server-and-test": "^1.14.0", "typescript": "^4.7.3" } }, @@ -2649,6 +2653,21 @@ "tslib": "^2.1.0" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -3178,9 +3197,9 @@ } }, "node_modules/@kingsrook/qqq-frontend-core": { - "version": "1.0.25", - "resolved": "https://npm.pkg.github.com/download/@Kingsrook/qqq-frontend-core/1.0.25/03014325ceda54cd984e44e0f650f0eff81ab536", - "integrity": "sha512-oO0ykxZnafzyomy8ZqZxiv5QVf0E5NIqvkXp2rUlNq44zt4Sx+93uMWl4IczcRCpbFpfaWxzpLxhdN/mjmMcUw==", + "version": "1.0.36", + "resolved": "https://npm.pkg.github.com/download/@Kingsrook/qqq-frontend-core/1.0.36/ddc9de26e08e365e999642619dacaaa290920370", + "integrity": "sha512-KC9Uj/1W87qxpBWLPe+hyJlPQiV43YAotguDUYkTytriNUhmSnTqVhyrC9crBxW4rj4+MX4IJXVFOqoh86A3Uw==", "license": "ISC", "dependencies": { "axios": "0.27.2", @@ -3847,6 +3866,27 @@ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "dev": true + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, "node_modules/@sinclair/typebox": { "version": "0.24.44", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.44.tgz", @@ -5131,6 +5171,11 @@ "node": ">= 0.6" } }, + "node_modules/ace-builds": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.12.3.tgz", + "integrity": "sha512-LHAsa5oOaRqmIlb8gBe81nj2kOqlfbV0XkWkFZL4mIfPXL4zoeTUcandHvBgHQCyjif3tGfoLTXelWSlnCT/dA==" + }, "node_modules/acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", @@ -7265,9 +7310,9 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/cypress": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.3.1.tgz", - "integrity": "sha512-As9HrExjAgpgjCnbiQCuPdw5sWKx5HUJcK2EOKziu642akwufr/GUeqL5UnCPYXTyyibvEdWT/pSC2qnGW/e5w==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-11.0.1.tgz", + "integrity": "sha512-NuEfd0Vim492RJ3m/+bbTZ3OZrqXgfAfuLaZfIQ9D5lKocS3EDr2tyAarZdAhKwLyoh7OJ33jwMeMFIDbzYqog==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -7290,7 +7335,7 @@ "dayjs": "^1.10.4", "debug": "^4.3.2", "enquirer": "^2.3.6", - "eventemitter2": "^6.4.3", + "eventemitter2": "6.4.7", "execa": "4.1.0", "executable": "^4.1.1", "extract-zip": "2.0.1", @@ -7321,6 +7366,12 @@ "node": ">=12.0.0" } }, + "node_modules/cypress-wait-for-stable-dom": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cypress-wait-for-stable-dom/-/cypress-wait-for-stable-dom-0.1.0.tgz", + "integrity": "sha512-iVJc6CDzlu1xUnTcZph+zbkOlImaDelpvRv4G+3naugvjkF6b9EFpDmRCC/16xL1pqpkFq4rFyfhuNw4C3PQjw==", + "dev": true + }, "node_modules/cypress/node_modules/@types/node": { "version": "14.18.32", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.32.tgz", @@ -7645,6 +7696,11 @@ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, "node_modules/diff-sequences": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", @@ -8913,10 +8969,25 @@ "node": ">= 0.6" } }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "node_modules/eventemitter2": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", - "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, "node_modules/eventemitter3": { @@ -9610,6 +9681,12 @@ "node": ">= 0.6" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -12183,6 +12260,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/joi": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.0.tgz", + "integrity": "sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/jquery": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", @@ -12622,6 +12712,16 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -12805,6 +12905,12 @@ "tmpl": "1.0.5" } }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -13523,6 +13629,15 @@ "node": ">=8" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -14984,6 +15099,21 @@ "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -15165,6 +15295,22 @@ "node": ">=0.10.0" } }, + "node_modules/react-ace": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz", + "integrity": "sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==", + "dependencies": { + "ace-builds": "^1.4.14", + "diff-match-patch": "^1.0.5", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-app-polyfill": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", @@ -16678,6 +16824,18 @@ "wbuf": "^1.7.3" } }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -16738,6 +16896,90 @@ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" }, + "node_modules/start-server-and-test": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.14.0.tgz", + "integrity": "sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw==", + "dev": true, + "dependencies": { + "bluebird": "3.7.2", + "check-more-types": "2.24.0", + "debug": "4.3.2", + "execa": "5.1.1", + "lazy-ass": "1.6.0", + "ps-tree": "1.2.0", + "wait-on": "6.0.0" + }, + "bin": { + "server-test": "src/bin/start.js", + "start-server-and-test": "src/bin/start.js", + "start-test": "src/bin/start.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/start-server-and-test/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/start-server-and-test/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/start-server-and-test/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/start-server-and-test/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -16746,6 +16988,15 @@ "node": ">= 0.8" } }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -17877,6 +18128,34 @@ "node": ">=10" } }, + "node_modules/wait-on": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.0.tgz", + "integrity": "sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw==", + "dev": true, + "dependencies": { + "axios": "^0.21.1", + "joi": "^17.4.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rxjs": "^7.1.0" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/wait-on/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -20410,6 +20689,21 @@ "tslib": "^2.1.0" } }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, "@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -20819,9 +21113,9 @@ } }, "@kingsrook/qqq-frontend-core": { - "version": "1.0.25", - "resolved": "https://npm.pkg.github.com/download/@Kingsrook/qqq-frontend-core/1.0.25/03014325ceda54cd984e44e0f650f0eff81ab536", - "integrity": "sha512-oO0ykxZnafzyomy8ZqZxiv5QVf0E5NIqvkXp2rUlNq44zt4Sx+93uMWl4IczcRCpbFpfaWxzpLxhdN/mjmMcUw==", + "version": "1.0.36", + "resolved": "https://npm.pkg.github.com/download/@Kingsrook/qqq-frontend-core/1.0.36/ddc9de26e08e365e999642619dacaaa290920370", + "integrity": "sha512-KC9Uj/1W87qxpBWLPe+hyJlPQiV43YAotguDUYkTytriNUhmSnTqVhyrC9crBxW4rj4+MX4IJXVFOqoh86A3Uw==", "requires": { "axios": "0.27.2", "form-data": "4.0.0" @@ -21201,6 +21495,27 @@ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, + "@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "dev": true + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, "@sinclair/typebox": { "version": "0.24.44", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.44.tgz", @@ -22211,6 +22526,11 @@ "negotiator": "0.6.3" } }, + "ace-builds": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.12.3.tgz", + "integrity": "sha512-LHAsa5oOaRqmIlb8gBe81nj2kOqlfbV0XkWkFZL4mIfPXL4zoeTUcandHvBgHQCyjif3tGfoLTXelWSlnCT/dA==" + }, "acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", @@ -23751,9 +24071,9 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "cypress": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.3.1.tgz", - "integrity": "sha512-As9HrExjAgpgjCnbiQCuPdw5sWKx5HUJcK2EOKziu642akwufr/GUeqL5UnCPYXTyyibvEdWT/pSC2qnGW/e5w==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-11.0.1.tgz", + "integrity": "sha512-NuEfd0Vim492RJ3m/+bbTZ3OZrqXgfAfuLaZfIQ9D5lKocS3EDr2tyAarZdAhKwLyoh7OJ33jwMeMFIDbzYqog==", "dev": true, "requires": { "@cypress/request": "^2.88.10", @@ -23775,7 +24095,7 @@ "dayjs": "^1.10.4", "debug": "^4.3.2", "enquirer": "^2.3.6", - "eventemitter2": "^6.4.3", + "eventemitter2": "6.4.7", "execa": "4.1.0", "executable": "^4.1.1", "extract-zip": "2.0.1", @@ -23838,6 +24158,12 @@ } } }, + "cypress-wait-for-stable-dom": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cypress-wait-for-stable-dom/-/cypress-wait-for-stable-dom-0.1.0.tgz", + "integrity": "sha512-iVJc6CDzlu1xUnTcZph+zbkOlImaDelpvRv4G+3naugvjkF6b9EFpDmRCC/16xL1pqpkFq4rFyfhuNw4C3PQjw==", + "dev": true + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -24038,6 +24364,11 @@ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, + "diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, "diff-sequences": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", @@ -24957,10 +25288,25 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "eventemitter2": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", - "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, "eventemitter3": { @@ -25481,6 +25827,12 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -27359,6 +27711,19 @@ } } }, + "joi": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.0.tgz", + "integrity": "sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "jquery": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", @@ -27706,6 +28071,16 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -27850,6 +28225,12 @@ "tmpl": "1.0.5" } }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -28358,6 +28739,15 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "requires": { + "through": "~2.3" + } + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -29240,6 +29630,15 @@ "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -29377,6 +29776,18 @@ "object-assign": "^4.1.1" } }, + "react-ace": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz", + "integrity": "sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==", + "requires": { + "ace-builds": "^1.4.14", + "diff-match-patch": "^1.0.5", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.7.2" + } + }, "react-app-polyfill": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", @@ -30536,6 +30947,15 @@ "wbuf": "^1.7.3" } }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "requires": { + "through": "2" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -30583,11 +31003,75 @@ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" }, + "start-server-and-test": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.14.0.tgz", + "integrity": "sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw==", + "dev": true, + "requires": { + "bluebird": "3.7.2", + "check-more-types": "2.24.0", + "debug": "4.3.2", + "execa": "5.1.1", + "lazy-ass": "1.6.0", + "ps-tree": "1.2.0", + "wait-on": "6.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + } + } + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -31440,6 +31924,30 @@ "xml-name-validator": "^3.0.0" } }, + "wait-on": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.0.tgz", + "integrity": "sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw==", + "dev": true, + "requires": { + "axios": "^0.21.1", + "joi": "^17.4.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rxjs": "^7.1.0" + }, + "dependencies": { + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.0" + } + } + } + }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 1b761c6..4aecab8 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@fullcalendar/interaction": "5.10.0", "@fullcalendar/react": "5.10.0", "@fullcalendar/timegrid": "5.10.0", - "@kingsrook/qqq-frontend-core": "1.0.35", + "@kingsrook/qqq-frontend-core": "1.0.36", "@mui/icons-material": "5.4.1", "@mui/material": "5.4.1", "@mui/styled-engine": "5.4.1", @@ -77,7 +77,8 @@ "prepublishOnly": "tsc -p ./ --outDir lib/", "start": "BROWSER=none react-scripts start", "test": "react-scripts test", - "cypress:open": "cypress open" + "cypress:open": "cypress open", + "cypress:run": "cypress run" }, "eslintConfig": { "extends": [ @@ -105,7 +106,8 @@ "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.10.2", "@typescript-eslint/parser": "5.10.2", - "cypress": "10.3.1", + "cypress": "11.0.1", + "cypress-wait-for-stable-dom": "0.1.0", "eslint": "8.8.0", "eslint-config-airbnb": "19.0.4", "eslint-import-resolver-typescript": "2.5.0", diff --git a/src/App.tsx b/src/App.tsx index 4e50dae..4510c97 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,6 +23,7 @@ import {useAuth0} from "@auth0/auth0-react"; import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException"; import {QAppNodeType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppNodeType"; import {QAppTreeNode} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppTreeNode"; +import {QAuthenticationMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAuthenticationMetaData"; import {QBrandingMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QBrandingMetaData"; import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; import CssBaseline from "@mui/material/CssBaseline"; @@ -86,6 +87,7 @@ function getStaticRoutes() ]; } +const qController = QClient.getInstance(); export const SESSION_ID_COOKIE_NAME = "sessionId"; LicenseInfo.setLicenseKey(process.env.REACT_APP_MATERIAL_UI_LICENSE_KEY); @@ -105,24 +107,51 @@ export default function App() return; } setLoadingToken(true); + (async () => { - try + const authenticationMetaData: QAuthenticationMetaData = await qController.getAuthenticationMetaData(); + + if (authenticationMetaData.type === "AUTH_0") { - console.log("Loading token..."); - await getAccessTokenSilently(); - const idToken = await getIdTokenClaims(); - setCookie(SESSION_ID_COOKIE_NAME, idToken.__raw, {path: "/"}); - setIsFullyAuthenticated(true); - console.log("Token load complete."); + ///////////////////////////////////////// + // use auth0 if auth type is ... auth0 // + ///////////////////////////////////////// + try + { + console.log("Loading token..."); + await getAccessTokenSilently(); + const idToken = await getIdTokenClaims(); + setCookie(SESSION_ID_COOKIE_NAME, idToken.__raw, {path: "/"}); + setIsFullyAuthenticated(true); + console.log("Token load complete."); + } + catch (e) + { + console.log(`Error loading token: ${JSON.stringify(e)}`); + removeCookie(SESSION_ID_COOKIE_NAME); + qController.clearAuthenticationMetaDataLocalStorage(); + logout(); + return; + } } - catch (e) + else if (authenticationMetaData.type === "FULLY_ANONYMOUS" || authenticationMetaData.type === "MOCK") { - console.log(`Error loading token: ${JSON.stringify(e)}`); - removeCookie(SESSION_ID_COOKIE_NAME); - logout(); + ///////////////////////////////////////////// + // use a random token if anonymous or mock // + ///////////////////////////////////////////// + console.log("Generating random token..."); + setIsFullyAuthenticated(true); + setCookie(SESSION_ID_COOKIE_NAME, Md5.hashStr(`${new Date()}`), {path: "/"}); + console.log("Token generation complete."); return; } + else + { + console.log(`Unrecognized authenticationMetaData.type: ${authenticationMetaData.type}`); + qController.clearAuthenticationMetaDataLocalStorage(); + } + })(); }, [loadingToken]); @@ -265,7 +294,7 @@ export default function App() routeList.push({ name: `${app.label}`, - key: `${app.name}.dev`, + key: `${app.name}.record.dev`, route: `${path}/:id/dev`, component: , }); @@ -342,13 +371,13 @@ export default function App() let profileRoutes = {}; const gravatarBase = "https://www.gravatar.com/avatar/"; - const hash = Md5.hashStr(user.email); + const hash = Md5.hashStr(user?.email || "user"); const profilePicture = `${gravatarBase}${hash}`; profileRoutes = { type: "collapse", - name: user.name, - key: user.name, - icon: , + name: user?.name, + key: "username", + icon: , collapse: [ { name: "My Profile", @@ -388,11 +417,16 @@ export default function App() } catch (e) { + console.error(e); if (e instanceof QException) { if ((e as QException).message.indexOf("status code 401") !== -1) { removeCookie(SESSION_ID_COOKIE_NAME); + + ////////////////////////////////////////////////////// + // todo - this is auth0 logout... make more generic // + ////////////////////////////////////////////////////// logout(); return; } diff --git a/src/HandleAuthorizationError.tsx b/src/HandleAuthorizationError.tsx index 0bedfa9..6f3556c 100644 --- a/src/HandleAuthorizationError.tsx +++ b/src/HandleAuthorizationError.tsx @@ -19,11 +19,10 @@ * along with this program. If not, see . */ -import {Auth0Provider, useAuth0} from "@auth0/auth0-react"; +import {useAuth0} from "@auth0/auth0-react"; import React, {useEffect} from "react"; import {useCookies} from "react-cookie"; import {SESSION_ID_COOKIE_NAME} from "App"; -import {AUTH0_CLIENT_ID, AUTH0_DOMAIN} from "index"; interface Props { @@ -44,11 +43,7 @@ function HandleAuthorizationError({errorMessage}: Props) }); return ( - -
-
{errorMessage}
-
-
+
{errorMessage}
); } diff --git a/src/index.tsx b/src/index.tsx index 95ebc2a..8b07d3b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -20,6 +20,7 @@ */ import {Auth0Provider} from "@auth0/auth0-react"; +import {QAuthenticationMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAuthenticationMetaData"; import React from "react"; import {render} from "react-dom"; import {BrowserRouter, useNavigate, useSearchParams} from "react-router-dom"; @@ -28,52 +29,73 @@ import "qqq/styles/qqq-override-styles.css"; import {MaterialUIControllerProvider} from "context"; import HandleAuthorizationError from "HandleAuthorizationError"; import ProtectedRoute from "qqq/auth0/protected-route"; +import QClient from "qqq/utils/QClient"; -export const AUTH0_DOMAIN = process.env.REACT_APP_AUTH0_DOMAIN; -export const AUTH0_CLIENT_ID = process.env.REACT_APP_AUTH0_CLIENT_ID; +const qController = QClient.getInstance(); +const authenticationMetaDataPromise: Promise = qController.getAuthenticationMetaData() -// @ts-ignore -function Auth0ProviderWithRedirectCallback({children, ...props}) +authenticationMetaDataPromise.then((authenticationMetaData) => { - const navigate = useNavigate(); - const [searchParams] = useSearchParams(); - // @ts-ignore - const onRedirectCallback = (appState) => + function Auth0ProviderWithRedirectCallback({children, ...props}) { - navigate((appState && appState.returnTo) || window.location.pathname); - }; - if (searchParams.get("error")) + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + + // @ts-ignore + const onRedirectCallback = (appState) => + { + navigate((appState && appState.returnTo) || window.location.pathname); + }; + if (searchParams.get("error")) + { + return ( + // @ts-ignore + + + + ); + } + else + { + return ( + // @ts-ignore + + {children} + + ); + } + } + + if (authenticationMetaData.type === "AUTH_0") { - return ( - // @ts-ignore - - - + const domain = process.env.REACT_APP_AUTH0_DOMAIN; + const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID; + + render( + + + + + + + , + document.getElementById("root"), ); } else { - return ( - // @ts-ignore - - {children} - - ); + render( + + + + + + , document.getElementById("root")); } -} -render( - - - - - - - , - document.getElementById("root"), -); +}) diff --git a/src/qqq/pages/entity-list/EntityList.tsx b/src/qqq/pages/entity-list/EntityList.tsx index 55ed266..bfcd30b 100644 --- a/src/qqq/pages/entity-list/EntityList.tsx +++ b/src/qqq/pages/entity-list/EntityList.tsx @@ -19,12 +19,9 @@ * along with this program. If not, see . */ -import {AdornmentType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/AdornmentType"; import {Capability} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Capability"; -import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; -import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria"; import {QFilterOrderBy} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterOrderBy"; import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter"; @@ -44,10 +41,9 @@ import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import Modal from "@mui/material/Modal"; import Tooltip from "@mui/material/Tooltip"; -import {DataGridPro, getGridDateOperators, GridCallbackDetails, GridColDef, GridColumnOrderChangeParams, GridColumnVisibilityModel, GridDensity, GridEventListener, GridExportMenuItemProps, GridFilterModel, GridLinkOperator, GridPinnedColumns, gridPreferencePanelStateSelector, GridRowId, GridRowParams, GridRowsProp, GridSelectionModel, GridSortItem, GridSortModel, GridState, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExportContainer, GridToolbarFilterButton, MuiEvent, useGridApiContext, useGridApiEventHandler, useGridSelector} from "@mui/x-data-grid-pro"; -import {GridFilterOperator} from "@mui/x-data-grid/models/gridFilterOperator"; +import {DataGridPro, GridCallbackDetails, GridColDef, GridColumnOrderChangeParams, GridColumnVisibilityModel, GridDensity, GridEventListener, GridExportMenuItemProps, GridFilterModel, GridLinkOperator, GridPinnedColumns, gridPreferencePanelStateSelector, GridRowId, GridRowParams, GridRowsProp, GridSelectionModel, GridSortItem, GridSortModel, GridState, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExportContainer, GridToolbarFilterButton, MuiEvent, useGridApiContext, useGridApiEventHandler, useGridSelector} from "@mui/x-data-grid-pro"; import React, {useContext, useEffect, useReducer, useRef, useState} from "react"; -import {Link, useLocation, useNavigate, useSearchParams} from "react-router-dom"; +import {useLocation, useNavigate, useSearchParams} from "react-router-dom"; import QContext from "QContext"; import DashboardLayout from "qqq/components/DashboardLayout"; import Footer from "qqq/components/Footer"; @@ -55,7 +51,6 @@ import Navbar from "qqq/components/Navbar"; import {QActionsMenuButton, QCreateNewButton} from "qqq/components/QButtons"; import MDAlert from "qqq/components/Temporary/MDAlert"; import MDBox from "qqq/components/Temporary/MDBox"; -import {buildQGridPvsOperators, QGridBooleanOperators, QGridNumericOperators, QGridStringOperators} from "qqq/pages/entity-list/QGridFilterOperators"; import ProcessRun from "qqq/pages/process-run"; import DataGridUtils from "qqq/utils/DataGridUtils"; import QClient from "qqq/utils/QClient"; @@ -237,7 +232,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element const [activeModalProcess, setActiveModalProcess] = useState(null as QProcessMetaData); const [launchingProcess, setLaunchingProcess] = useState(launchProcess); - const [recordIdsForProcess, setRecordIdsForProcess] = useState(null as string | QQueryFilter) + const [recordIdsForProcess, setRecordIdsForProcess] = useState(null as string | QQueryFilter); const instance = useRef({timer: null}); @@ -355,13 +350,26 @@ function EntityList({table, launchProcess}: Props): JSX.Element return qFilter; }; + const getTableMetaData = async (): Promise => + { + if(tableMetaData !== null) + { + return(new Promise((resolve) => + { + resolve(tableMetaData) + })); + } + + return (qController.loadTableMetaData(tableName)); + } + const updateTable = () => { setLoading(true); setRows([]); (async () => { - const tableMetaData = await qController.loadTableMetaData(tableName); + const tableMetaData = await getTableMetaData(); setPageHeader(tableMetaData.label); //////////////////////////////////////////////////////////////////////////////////////////////// @@ -530,7 +538,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element const handlePinnedColumnsChange = (pinnedColumns: GridPinnedColumns) => { - setPinnedColumns(pinnedColumns) + setPinnedColumns(pinnedColumns); localStorage.setItem(pinnedColumnsLocalStorageKey, JSON.stringify(pinnedColumns)); }; @@ -1080,7 +1088,14 @@ function EntityList({table, launchProcess}: Props): JSX.Element /////////////////////////////////////////////////////////////////////////////////////////// useEffect(() => { - updateTable(); + if(latestQueryId > 0) + { + //////////////////////////////////////////////////////////////////////////////////////// + // to avoid both this useEffect and the one below from both doing an "initial query", // + // only run this one if at least 1 query has already been ran // + //////////////////////////////////////////////////////////////////////////////////////// + updateTable(); + } }, [ pageNumber, rowsPerPage, columnSortModel ]); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/qqq/utils/QClient.ts b/src/qqq/utils/QClient.ts index aff6d58..96272f5 100644 --- a/src/qqq/utils/QClient.ts +++ b/src/qqq/utils/QClient.ts @@ -32,6 +32,7 @@ class QClient private static handleException(exception: QException) { + // todo - check for 401 and clear cookie et al & logout? console.log(`Caught Exception: ${JSON.stringify(exception)}`); throw (exception); } diff --git a/src/qqq/utils/QProcessUtils.ts b/src/qqq/utils/QProcessUtils.ts index f7b2e6d..06e82a0 100644 --- a/src/qqq/utils/QProcessUtils.ts +++ b/src/qqq/utils/QProcessUtils.ts @@ -32,30 +32,36 @@ class QProcessUtils public static getProcessesForTable(metaData: QInstance, tableName: string, includeHidden = false): QProcessMetaData[] { const matchingProcesses: QProcessMetaData[] = []; - const processKeys = [...metaData.processes.keys()]; - processKeys.forEach((key) => + if (metaData.processes) { - const process = metaData.processes.get(key); - if (process.tableName === tableName && (includeHidden || !process.isHidden)) + const processKeys = [...metaData.processes.keys()]; + processKeys.forEach((key) => { - matchingProcesses.push(process); - } - }); + const process = metaData.processes.get(key); + if (process.tableName === tableName && (includeHidden || !process.isHidden)) + { + matchingProcesses.push(process); + } + }); + } return matchingProcesses; } public static getReportsForTable(metaData: QInstance, tableName: string, includeHidden = false): QReportMetaData[] { const matchingReports: QReportMetaData[] = []; - const reportKeys = [...metaData.reports.keys()]; - reportKeys.forEach((key) => + if (metaData.reports) { - const process = metaData.reports.get(key); - if (process.tableName === tableName) + const reportKeys = [...metaData.reports.keys()]; + reportKeys.forEach((key) => { - matchingReports.push(process); - } - }); + const process = metaData.reports.get(key); + if (process.tableName === tableName) + { + matchingReports.push(process); + } + }); + } return matchingReports; }