Metabase: cannot-join-two-saved-questions

e2e/test/scenarios/joins/reproductions/18512-cannot-join-two-saved-questions-with-same-implicit-explicit-grouped-field.cy.spec.js
Replay of localhost
e2e/test/scenarios/joins/reproductions/18512-cannot-join-two-saved-questions-with-same-implicit-explicit-grouped-field.cy.spec.js

What is the test trying to do?

The test creates two questions and then tries to join them with the same implicit/explicit grouped field.
Procedurally, the test performs these steps
  1. two questions are created via the API
  1. the test clicks on Saved Questions and then selects question 18512#1
  1. clicks the join button
  1. clicks on Saved Questions a second time and then selects question 18512#2
  1. visits the visualize page and waits for Products \u2192 Created At to appear.

What goes wrong?

When the test fails, the test is not able to select the second question.

What is the observable difference?

Image without caption
When the test fails, the “Saved Questions” button is missing so there’s nothing to click. This happens because the render function has a case statement that renders the TablePicker when the test fails and the DatabaseSchemaPicker when the test passes.
view replay
view replay

Why does the test fail?

The DatabaseSchemaPicker is rendered because the active step is a SCHEMA_STEP. And when the test fails, onChangeSchema is called with 1:PUBLIC. When the test passes, onChangeSchema is not called!
  1. Why is onChangeSchema called when the test fails?
    1. onChangeSchema is called from skipTests when the DataSelector is mounted and when the test fails, the component has 1:PUBLIC schema set in its state. When the test passes, the component does not have any schemas set.
view replay
view replay
  1. Why is the schema not set when the test passes?
    1. When the test passes, the schema is fetched when the DataSelector is mounted. Here’s what the console logs look like. Here’s the point where the fetch is returned.
Image without caption
When the test fails, the schema is fetched earlier which causes it to finish before the DataSelector even tries to fetch the schema. Here’s the point where the fetch is returned.
Image without caption
  1. Why is the schema fetched when the test fails
    1. When the test fails, the schema is fetched by an earlier test step. Here’s what we know:
    2. An earlier step triggers the DataSelector handleSavedQuestionSelect callback
    3. Which calls updateQuestion
    4. Which calls loadMetadataForCard
    5. Which calls entity.api.list
    6. Which calls MetabaseApi.db_schemas
    7. Which makes an api call

How do you fix the test?

This test failure is a example of how coupling React component rendering and state management can lead to unpredictable behavior.
There are two ways to fix this issue
  1. Add guards in the test to wait for the API calls to complete
  1. Add guards in the application so that the React component can re-render when the api call resolves
In this case, improving the application is the better option because this issue could just as easily effect users in production.