Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly identify test executions by utilizing repeatEachIndex #536

Merged
merged 17 commits into from
Jun 19, 2024

Conversation

Andarist
Copy link
Member

No description provided.

Copy link

changeset-bot bot commented Jun 17, 2024

🦋 Changeset detected

Latest commit: e43bde8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@replayio/test-utils Major
replayio Minor
@replayio/playwright Minor
@replayio/cypress Patch
@replayio/jest Patch
@replayio/puppeteer Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

replay-io bot commented Jun 17, 2024

Status Complete ↗︎
Commit c1a20fc
Results
2 Failed
  • clicks a disappearing button
  • should fail on this test
  • 42 Passed
  • adds items
  • adds new items using a custom command
  • adds todos following the fixture
  • adds todos following the fixture
  • adds todos following the fixture
  • adds todos following the fixture
  • calls inform
  • complete all checkbox should update state when items are completed / cleared
  • gets a number
  • only gets a number
  • only gets a number
  • should allow me to add todo items
  • should allow me to clear the complete state of all items
  • should allow me to display active items
  • should allow me to display all items
  • should allow me to display completed items
  • should allow me to edit an item
  • should allow me to mark all items as completed
  • should allow me to mark items as complete
  • should allow me to un-mark items as complete
  • should append new items to the bottom of the list
  • should be hidden when there are no items that are completed
  • should cancel edits on escape
  • should clear text input field when an item is added
  • should display the correct text
  • should display the current number of todo items
  • should focus on the todo input field
  • should hide #main and #footer
  • should hide other controls when editing
  • should highlight the currently applied filter
  • should intercept postman
  • should invoke some commands that have exceptional option handling
  • should log
  • should persist its data
  • should remove completed items when clicked
  • should remove the item if an empty text string was entered
  • should respect the back button
  • should save edits on blur
  • should show #main and #footer when items added
  • should trim entered text
  • should trim text input
  • yields a number
  • @Andarist Andarist marked this pull request as ready for review June 17, 2024 16:45
    @Andarist Andarist requested review from bvaughn and hbenl June 17, 2024 16:45
    @Andarist
    Copy link
    Member Author

    /release-pr

    @Andarist
    Copy link
    Member Author

    @bvaughn @hbenl it's good to be reviewed now

    Comment on lines 150 to 151
    // TODO: this could be simplified to `${test.testId}-${test.repeatEachIndex}-${test.attempt}`
    // before doing that all recipients of `TestExecutionIdData` should be rechecked to see if such a change would be safe
    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Can you file a Linear follow-up issue for this and update the TODO comment to reference it?

    Copy link
    Member Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    done

    // even when `minimizeUploads` is combined with `repeatEach` it always makes sense to upload the latest result
    // otherwise, we could upload a single successful attempt without uploading a potential failure of the same test
    // coming from a different `repeatEachIndex`
    toUpload = this._minimizeUploads ? [latestExecution] : executions;
    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    This looks wrong to me.

    What if the test was flaky? We're only getting the passing recording this way.

    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I wonder if this should be rewritten so that we have the pass/fail/flaky check on the outside and then the minimize check within?

    Copy link
    Member Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    What if the test was flaky? We're only getting the passing recording this way.

    You are correct. I'll fix this.

    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Something like this?

    const status = computeStatus();
    switch (status) {
      case "failed": {
        switch (this._uploadStatusThreshold) {
          case "all":
          case "failed":
          case "failed-and-flaky": {
            toUpload = this._minimizeUploads ? [latestExecution] : executions;
            break;
          }
        }
        break;
      }
      case "flaky": {
        switch (this._uploadStatusThreshold) {
          case "all":
          case "failed-and-flaky": {
            if (this._minimizeUploads) {
              toUpload = [executions[0], executions[executions.length - 1]];
            } else {
              toUpload = executions;
            }
            break;
          }
        }
        break;
      }
      case "pass": {
        switch (this._uploadStatusThreshold) {
          case "all": {
            toUpload = this._minimizeUploads ? [latestExecution] : executions;
            break;
          }
        }
        break;
      }
    }

    Copy link
    Member Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    This function uses executions from a single execution group (that currently is equivalent to repeatEachIndex) but the uploads are minimized per test id.

    We'd have an easier time if we'd push this whole decision until onEnd (that runs after everything gets completed). I decided to keep it in onTestEnd to start uploads asap - so we only have to await those upload jobs in onEnd. Maybe it's not worth it but it felt nice to have it "paralellized".

    We can't determine if a test case is flaky based on a signal execution group because both could have only single executions and each of them could be of a different status. I thought about having an aggregate status on this uploadable result but it felt like a complication at a time.

    The version above misses that those flakes can be spread across execution groups. I like your suggestion to reorganize this, I'll try to come up with something better.

    Copy link
    Member Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I have rewritten it based on ur suggestion. I think it's much cleaner now but the end result isn't exactly the same as your snippet, could u take another look?

    result.didUploadStatuses.failed ||= executions.some(r => r.result !== "passed");
    result.didUploadStatuses.passed ||= executions.some(r => r.result === "passed");

    // currently previously completed execution groups that could be entirely passed or failed are not retroactively uploaded here
    Copy link
    Member Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I don't think it's particularly important to "fix" this right now but I could certainly change my mind.

    If we have such execution groups, we detect flake when the third group completes:

    [passed]
    [passed]
    [failed, passed]
    

    We could look for all completed groups and upload them but it doesn't feel overly important to me.

    Copy link
    Contributor

    @bvaughn bvaughn left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I think this needs to handle the "none" case as mentioned

    Comment on lines +960 to +969
    if (!this._minimizeUploads) {
    result.didUploadStatuses.failed = true;
    toUpload.push(...executions);
    break;
    }
    if (result.didUploadStatuses.failed) {
    break;
    }
    result.didUploadStatuses.failed = true;
    toUpload.push(latestExecution);
    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I think we need to check if status threshold is "none" here before uploading?

    Suggested change
    if (!this._minimizeUploads) {
    result.didUploadStatuses.failed = true;
    toUpload.push(...executions);
    break;
    }
    if (result.didUploadStatuses.failed) {
    break;
    }
    result.didUploadStatuses.failed = true;
    toUpload.push(latestExecution);
    if (this._uploadStatusThreshold !== "none") {
    if (this._minimizeUploads) {
    if (!result.didUploadStatuses.failed) {
    result.didUploadStatuses.failed = true;
    toUpload.push(latestExecution);
    }
    } else {
    result.didUploadStatuses.failed = true;
    toUpload.push(...executions);
    }
    }

    TBH I guess we could just return early from this function if that's the status because the "flaky" branch below also needs to respect that condition.

    toUpload = [latestResult];
    }
    case "flaky": {
    if (this._uploadStatusThreshold === "failed") {
    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Unless we return early from this function I think we also need to check for "none" here too


    if (passedExecution) {
    result.didUploadStatuses.failed = true;
    toUpload.push(passedExecution);
    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    The way this code is organized is a little hard for me to read but I think it looks correct.

    Comment on lines 1009 to 1023
    case "passed": {
    if (this._uploadStatusThreshold !== "all") {
    break;
    }
    if (!this._minimizeUploads) {
    result.didUploadStatuses.passed = true;
    toUpload.push(...executions);
    break;
    }
    if (result.didUploadStatuses.passed) {
    break;
    }
    result.didUploadStatuses.passed = true;
    toUpload.push(latestExecution);
    break;
    Copy link
    Contributor

    @bvaughn bvaughn Jun 18, 2024

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I find interleaved break statements a little harder to read. I think it's because it feels like more work to have to keep track of all of the handled conditions in my mind as I read further along. IF/ELSE blocks feel like they provide scaffolding to make that easier.

    @Andarist
    Copy link
    Member Author

    I think this needs to handle the "none" case as mentioned

    We never get to those functions with this threshold as there is an early return here:

    if (this._uploadStatusThreshold === "none") {
    return;
    }

    @bvaughn
    Copy link
    Contributor

    bvaughn commented Jun 18, 2024

    I think this needs to handle the "none" case as mentioned

    We never get to those functions with this threshold as there is an early return here:

    if (this._uploadStatusThreshold === "none") {
    return;
    }

    I don't think that's a strong enough guard. This method could be called from other places (even if it isn't currently) so I think it needs its own guard. (Avoid relying on less obvious / indirect checks for things like this is a lesson I've learned for myself over the years, not to depend on indirect or less obvious short-circuits for things like this.)

    @Andarist Andarist merged commit 8343abe into main Jun 19, 2024
    7 checks passed
    @Andarist Andarist deleted the andarist/pro-611-support-repeateach-in-playwright branch June 19, 2024 10:18
    @github-actions github-actions bot mentioned this pull request Jun 19, 2024
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    None yet
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    2 participants