diff --git a/frontend/test/__support__/integrated_tests.js b/frontend/test/__support__/integrated_tests.js index b6331c3e7bd84ec3c07ccfd0c71a637874c2ee6b..712b26dd9974072427f6950c26486178978d3117 100644 --- a/frontend/test/__support__/integrated_tests.js +++ b/frontend/test/__support__/integrated_tests.js @@ -191,23 +191,24 @@ const testStoreEnhancer = (createStore, history, getRoutes) => { const testStoreExtensions = { _originalDispatch: store.dispatch, _onActionDispatched: null, - _dispatchedActions: [], + _allDispatchedActions: [], + _latestDispatchedActions: [], _finalStoreInstance: null, dispatch: (action) => { const result = store._originalDispatch(action); - store._dispatchedActions = store._dispatchedActions.concat([{ + + const actionWithTimestamp = [{ ...action, timestamp: Date.now() - }]); + }] + store._allDispatchedActions = store._allDispatchedActions.concat(actionWithTimestamp); + store._latestDispatchedActions = store._latestDispatchedActions.concat(actionWithTimestamp); + if (store._onActionDispatched) store._onActionDispatched(); return result; }, - resetDispatchedActions: () => { - store._dispatchedActions = []; - }, - /** * Waits until all actions with given type identifiers have been called or fails if the maximum waiting * time defined in `timeout` is exceeded. @@ -221,8 +222,14 @@ const testStoreEnhancer = (createStore, history, getRoutes) => { actionTypes = Array.isArray(actionTypes) ? actionTypes : [actionTypes] + // Returns all actions that are triggered after the last action which belongs to `actionTypes + const getRemainingActions = () => { + const lastActionIndex = _.findLastIndex(store._latestDispatchedActions, (action) => actionTypes.includes(action.type)) + return store._latestDispatchedActions.slice(lastActionIndex + 1) + } + const allActionsAreTriggered = () => _.every(actionTypes, actionType => - store._dispatchedActions.filter((action) => action.type === actionType).length > 0 + store._latestDispatchedActions.filter((action) => action.type === actionType).length > 0 ); if (allActionsAreTriggered()) { @@ -232,6 +239,7 @@ const testStoreEnhancer = (createStore, history, getRoutes) => { return new Promise((resolve, reject) => { store._onActionDispatched = () => { if (allActionsAreTriggered()) { + store._latestDispatchedActions = getRemainingActions(); store._onActionDispatched = null; resolve() } @@ -241,14 +249,17 @@ const testStoreEnhancer = (createStore, history, getRoutes) => { if (allActionsAreTriggered()) { // TODO: Figure out why we sometimes end up here instead of _onActionDispatched hook + store._latestDispatchedActions = getRemainingActions(); resolve() } else { return reject( new Error( `These actions were not dispatched within ${timeout}ms:\n` + chalk.cyan(actionTypes.join("\n")) + - "\n\nDispatched actions since initialization / last call of `store.resetDispatchedActions()`:\n" + - (store._dispatchedActions.map(store._formatDispatchedAction).join("\n") || "No dispatched actions") + "\n\nDispatched actions since the last call of `waitForActions`:\n" + + (store._latestDispatchedActions.map(store._formatDispatchedAction).join("\n") || "No dispatched actions") + + "\n\nDispatched actions since the initialization of test suite:\n" + + (store._allDispatchedActions.map(store._formatDispatchedAction).join("\n") || "No dispatched actions") ) ) } @@ -260,8 +271,10 @@ const testStoreEnhancer = (createStore, history, getRoutes) => { logDispatchedActions: () => { console.log( - chalk.bold("\n\nDispatched actions since initialization / last call of `store.resetDispatchedActions()`:\n") + - store._dispatchedActions.map(store._formatDispatchedAction).join("\n") || "No dispatched actions" + chalk.bold("Dispatched actions since last call of `waitForActions`:\n") + + (store._latestDispatchedActions.map(store._formatDispatchedAction).join("\n") || "No dispatched actions") + + chalk.bold("\n\nDispatched actions since initialization of test suite:\n") + + store._allDispatchedActions.map(store._formatDispatchedAction).join("\n") || "No dispatched actions" ) }, diff --git a/frontend/test/admin/datamodel/FieldApp.integ.spec.js b/frontend/test/admin/datamodel/FieldApp.integ.spec.js index cad966f5918a678d1aa5afac16aa538e45bd4a55..f43cd62ef9fc2c4dd5de70a6590a7e51e623fae9 100644 --- a/frontend/test/admin/datamodel/FieldApp.integ.spec.js +++ b/frontend/test/admin/datamodel/FieldApp.integ.spec.js @@ -61,7 +61,6 @@ const initFieldApp = async ({ tableId = 1, fieldId }) => { store.pushPath(`/admin/datamodel/database/1/table/${tableId}/${fieldId}`); const fieldApp = mount(store.connectContainer(<FieldApp />)); await store.waitForActions([FETCH_IDFIELDS]); - store.resetDispatchedActions(); return { store, fieldApp } } @@ -87,7 +86,6 @@ describe("FieldApp", () => { setInputValue(nameInput, newTitle); await store.waitForActions([UPDATE_FIELD]) - store.resetDispatchedActions(); setInputValue(descriptionInput, newDescription); await store.waitForActions([UPDATE_FIELD]) @@ -200,7 +198,6 @@ describe("FieldApp", () => { const foreignKeyButton = typeSelect.find(TestPopover).find("li").at(2).children().first(); click(foreignKeyButton); await store.waitForActions([UPDATE_FIELD]) - store.resetDispatchedActions(); expect(picker.text()).toMatch(/Foreign KeySelect a target/); const fkFieldSelect = picker.find(Select).at(1) @@ -254,7 +251,6 @@ describe("FieldApp", () => { const useFKButton = pickerOptions.at(1).children().first() click(useFKButton); store.waitForActions([UPDATE_FIELD_DIMENSION, FETCH_TABLE_METADATA]) - store.resetDispatchedActions(); // TODO: Figure out a way to avoid using delay – the use of delays may lead to occasional CI failures await delay(500); diff --git a/frontend/test/admin/datamodel/datamodel.integ.spec.js b/frontend/test/admin/datamodel/datamodel.integ.spec.js index 213685b78a04ba92cc49293fd5583566d1081d34..bf9ab6dc76efdbfce49ddd696754d6e1220ca0a6 100644 --- a/frontend/test/admin/datamodel/datamodel.integ.spec.js +++ b/frontend/test/admin/datamodel/datamodel.integ.spec.js @@ -51,17 +51,14 @@ describe("admin/datamodel", () => { const adminListItems = app.find(".AdminList-item"); click(adminListItems.at(0)); await store.waitForActions([SELECT_TABLE]); - store.resetDispatchedActions() // Toggle its visibility to "Hidden" click(app.find("#VisibilityTypes > span").at(1)) await store.waitForActions([UPDATE_TABLE]); - store.resetDispatchedActions() // Toggle "Why hide" to "Irrelevant/Cruft" click(app.find("#VisibilitySubTypes > span").at(2)) await store.waitForActions([UPDATE_TABLE]); - store.resetDispatchedActions() // Unhide click(app.find("#VisibilityTypes > span").at(0)) @@ -69,7 +66,6 @@ describe("admin/datamodel", () => { // Open "People" table section click(adminListItems.at(1)); await store.waitForActions([SELECT_TABLE]); - store.resetDispatchedActions() // hide fields from people table // Set Address field to "Only in Detail Views" @@ -80,7 +76,6 @@ describe("admin/datamodel", () => { expect(onlyInDetailViewsRow.text()).toMatch(/Only in Detail Views/); click(onlyInDetailViewsRow); await store.waitForActions([UPDATE_FIELD]); - store.resetDispatchedActions(); // Set Birth Date field to "Do Not Include" click(columnsListItems.at(1).find(".TableEditor-field-visibility")); @@ -90,7 +85,6 @@ describe("admin/datamodel", () => { click(doNotIncludeRow); await store.waitForActions([UPDATE_FIELD]); - store.resetDispatchedActions(); // modify special type for address field click(columnsListItems.first().find(".TableEditor-field-special-type")) @@ -110,13 +104,11 @@ describe("admin/datamodel", () => { const app = mount(store.getAppContainer()) await store.waitForActions([INITIALIZE_METADATA, FETCH_IDFIELDS]); - store.resetDispatchedActions(); // Click the new segment button and check that we get properly redirected click(app.find(SegmentsList).find(Link)); expect(store.getPath()).toBe('/admin/datamodel/segment/create?table=2') await store.waitForActions([FETCH_TABLE_METADATA, UPDATE_PREVIEW_SUMMARY]); - store.resetDispatchedActions(); // Add "Email Is Not gmail" filter click(app.find(".GuiBuilder-filtered-by a").first()) @@ -142,7 +134,6 @@ describe("admin/datamodel", () => { click(app.find('button[children="Save changes"]')) await store.waitForActions([CREATE_SEGMENT, INITIALIZE_METADATA]); - store.resetDispatchedActions(); expect(store.getPath()).toBe("/admin/datamodel/database/1/table/2") // Validate that the segment got actually added @@ -157,7 +148,6 @@ describe("admin/datamodel", () => { const app = mount(store.getAppContainer()) await store.waitForActions([INITIALIZE_METADATA, FETCH_IDFIELDS]); - store.resetDispatchedActions(); // Click the new metric button and check that we get properly redirected click(app.find(MetricsList).find(Link)); diff --git a/frontend/test/parameters/parameters.integ.spec.js b/frontend/test/parameters/parameters.integ.spec.js index b57e4d2b112de859de85c946b5aeccbb9abeb8c3..98b586db144fba1b56709c2244a0603ce25d01bc 100644 --- a/frontend/test/parameters/parameters.integ.spec.js +++ b/frontend/test/parameters/parameters.integ.spec.js @@ -126,7 +126,6 @@ describe("parameters", () => { click(app.find(".Icon-sql")); await store.waitForActions([SET_QUERY_MODE]); - store.resetDispatchedActions(); await updateQueryText(store, "select count(*) from products where {{category}}"); @@ -137,7 +136,6 @@ describe("parameters", () => { click(fieldFilterVarType); await store.waitForActions([UPDATE_TEMPLATE_TAG]); - store.resetDispatchedActions(); await delay(100); @@ -154,7 +152,6 @@ describe("parameters", () => { // test without the parameter click(app.find(RunButton)); await store.waitForActions([RUN_QUERY, QUERY_COMPLETED]) - await store.resetDispatchedActions(); expect(app.find(Scalar).text()).toBe(COUNT_ALL); // test the parameter @@ -215,7 +212,6 @@ describe("parameters", () => { const app = mount(store.getAppContainer()) await store.waitForActions([ADD_PARAM_VALUES]); - store.resetDispatchedActions(); // Loading the query results is done in PublicQuestion itself so we have to add a delay here await delay(200); diff --git a/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js b/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js index d721eafbcfe2d36a06520a09d6aec06b6441d4ab..404e74e26a4a28d2a741cdeaa808c0ee706cf760 100644 --- a/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js +++ b/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js @@ -67,7 +67,6 @@ describe("FieldPane", () => { click(getUseForButton()); await store.waitForActions([QUERY_COMPLETED]); - store.resetDispatchedActions() // after the breakout has been applied, the button shouldn't be visible anymore expect(getUseForButton().length).toBe(0); @@ -84,7 +83,6 @@ describe("FieldPane", () => { } await store.waitForActions([QUERY_COMPLETED]); - store.resetDispatchedActions() expect(queryBuilder.find(Table).length).toBe(1) }); diff --git a/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js b/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js index 8697aeb67be15fcd161e84bbe609859b7329caec..c2e37b1ec0e95e61c54449b550f8792829001103 100644 --- a/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js +++ b/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js @@ -50,7 +50,6 @@ describe("MetricPane", () => { // then we can replace this with `store.waitForActions([FETCH_TABLE_FOREIGN_KEYS])` or similar await delay(3000) - store.resetDispatchedActions() // make sure that we wait for the newest actions click(dataReference.find(`a[children="${vendor_count_metric.name}"]`).first()) await store.waitForActions([FETCH_TABLE_METADATA]); diff --git a/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js b/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js index 20fdd89fb458b0ffa766abff38f8a246274054c8..b4a3770b231898ddc38bfa238b2c22d6442bca9d 100644 --- a/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js +++ b/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js @@ -64,7 +64,6 @@ describe("SegmentPane", () => { // then we can replace this with `store.waitForActions([FETCH_TABLE_FOREIGN_KEYS])` or similar await delay(3000) - store.resetDispatchedActions() // make sure that we wait for the newest actions click(dataReference.find(`a[children="${orders_past_30_days_segment.name}"]`).first()) await store.waitForActions([FETCH_TABLE_METADATA]); @@ -84,7 +83,6 @@ describe("SegmentPane", () => { click(filterByButton.children().first()); await store.waitForActions([QUERY_COMPLETED]); - store.resetDispatchedActions() expect(queryBuilder.find(DataReference).find(UseForButton).length).toBe(0); }); @@ -100,7 +98,6 @@ describe("SegmentPane", () => { } await store.waitForActions([QUERY_COMPLETED]); - store.resetDispatchedActions() // The value changes daily which wasn't originally taken into account // expect(queryBuilder.find(Scalar).text()).toBe("1,236") @@ -118,7 +115,6 @@ describe("SegmentPane", () => { } await store.waitForActions([QUERY_COMPLETED]); - store.resetDispatchedActions() expect(queryBuilder.find(Table).length).toBe(1) }); diff --git a/frontend/test/query_builder/query_builder.integ.spec.js b/frontend/test/query_builder/query_builder.integ.spec.js index a14cbfca8f69b874f53f5717640474b4c0c5a458..302907556e889e671cdbcd9649f65c9c2a02287c 100644 --- a/frontend/test/query_builder/query_builder.integ.spec.js +++ b/frontend/test/query_builder/query_builder.integ.spec.js @@ -75,7 +75,6 @@ const initQbWithDbAndTable = (dbId, tableId) => { store.dispatch(setQueryDatabase(dbId)); store.dispatch(setQuerySourceTable(tableId)); await store.waitForActions([FETCH_TABLE_METADATA]); - store.resetDispatchedActions(); return { store, qb } } @@ -307,7 +306,6 @@ describe("QueryBuilder", () => { clickButton(addFilterButton); await store.waitForActions([SET_DATASET_QUERY]) - store.resetDispatchedActions(); expect(qb.find(FilterPopover).length).toBe(0); const filterWidget = qb.find(FilterWidget); @@ -385,7 +383,6 @@ describe("QueryBuilder", () => { clickButton(addFilterButton); await store.waitForActions([SET_DATASET_QUERY]) - store.resetDispatchedActions(); expect(qb.find(FilterPopover).length).toBe(0); const filterWidget = qb.find(FilterWidget); @@ -492,7 +489,6 @@ describe("QueryBuilder", () => { expect(breakoutWidget.text()).toBe("Total: 100 bins"); }); it("produces correct results for 100 bins", async () => { - store.resetDispatchedActions(); click(qb.find(RunButton)); await store.waitForActions([QUERY_COMPLETED]); @@ -515,7 +511,6 @@ describe("QueryBuilder", () => { click(qb.find(DimensionPicker).find('a[children="Don\'t bin"]')); }); it("produces the expected count of rows when no binning", async () => { - store.resetDispatchedActions(); click(qb.find(RunButton)); await store.waitForActions([QUERY_COMPLETED]); @@ -585,7 +580,6 @@ describe("QueryBuilder", () => { }); it("produces correct results for 'Bin every 1 degree'", async () => { // Run the raw data query - store.resetDispatchedActions(); click(qb.find(RunButton)); await store.waitForActions([QUERY_COMPLETED]); @@ -631,7 +625,6 @@ describe("QueryBuilder", () => { // Drill-through is delayed in handleVisualizationClick of Visualization.jsx by 100ms await delay(150); - store.resetDispatchedActions(); click(qb.find(ChartClickActions).find('div[children="Zoom in"]')); store.waitForActions([NAVIGATE_TO_NEW_CARD, UPDATE_URL, QUERY_COMPLETED]); @@ -673,7 +666,6 @@ describe("QueryBuilder", () => { // Drill-through is delayed in handleVisualizationClick of Visualization.jsx by 100ms await delay(150); - store.resetDispatchedActions(); click(qb.find(ChartClickActions).find('div[children="Zoom in"]')); store.waitForActions([NAVIGATE_TO_NEW_CARD, UPDATE_URL, QUERY_COMPLETED]); @@ -719,7 +711,6 @@ describe("QueryBuilder", () => { // Drill-through is delayed in handleVisualizationClick of Visualization.jsx by 100ms await delay(150); - store.resetDispatchedActions(); click(qb.find(ChartClickActions).find('div[children="Zoom in"]')); store.waitForActions([NAVIGATE_TO_NEW_CARD, UPDATE_URL, QUERY_COMPLETED]); @@ -797,7 +788,6 @@ describe("QueryBuilder", () => { clickButton(addFilterButton); await store.waitForActions([SET_DATASET_QUERY]) - store.resetDispatchedActions(); // validate the filter text value expect(qb.find(FilterPopover).length).toBe(0);