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

visit() throws TransitionAborted error #332

Open
simonihmig opened this issue Feb 28, 2018 · 42 comments
Open

visit() throws TransitionAborted error #332

simonihmig opened this issue Feb 28, 2018 · 42 comments

Comments

@simonihmig
Copy link
Contributor

simonihmig commented Feb 28, 2018

As discussed in Slack, I was unexpectedly seeing failing tests where a transition was aborted.
ApplicationInstance#visit is throwing it here

As I said I cannot share the app, but this is the human readable test spec (we have a rather exotic test setup with ember-cli-yadda 😉):

  Scenario: Access restricted page as invited user and login
    Given there exists a user with username "testname" and password "testpw"
    And I am an invited user
    And I am on the homepage
    When I go to the user profile page
    Then I should still be on the homepage
    And I should see the login modal
    When I insert "testname" as the username
    And I insert "testpw" as the password
    And I submit the login form
    Then I should be on the user profile page

The I go to the user profile page step is throwing the exception, which is calling visit('/user/profile') internally.

I quickly assembled this little repro, that resembles what we were doing in a very minimal way:
https://github.com/simonihmig/ember-visit-transition-abort

All the acceptance tests are passing, with the exception of this last one:
https://github.com/simonihmig/ember-visit-transition-abort/blob/master/tests/acceptance/auth-test.js#L36-L40

@simonihmig
Copy link
Contributor Author

@rwjblue did you have a chance to look at this?

I am happy to work on a PR, if we agree what the solution should be. (this is the only remaining issue left for our app's test suite refactoring PR ;))

I am not fully aware of the inner workings, but it puzzles me that clicking on the link-to is different to using visit. (See this vs. that)

@spencer516
Copy link
Contributor

spencer516 commented May 8, 2018

@simonihmig — we've been running into this too. Just opened a PR with a failing test and a place to start: #373

(I'd be curious if it fixes your issue)

@esbanarango
Copy link

☝️ We're having the same issue when migrating to the new testing API.

@NullVoxPopuli
Copy link
Sponsor Collaborator

NullVoxPopuli commented Aug 19, 2018

I have some transitionTos happening in my afterModel hook -- which is intentional -- but in my test visit is raising a TransitionAborted exception.

The only way for my test to pass is:

try {
  await visit(url);
} catch(e) {
  console.error(e);
}

@ppcano
Copy link

ppcano commented Aug 21, 2018

I am also experiencing this issue when migrating our test suite (Ember 2.17 & ember-cli-qunit ^4.1.1)

We are using transition.abort and transition.retry to handle route authentication at the route level as suggested on the Preventing and Retrying Transitions guide.

Based on @NullVoxPopuli guidance, our current workaround has been to implement a wrapper of the visit helper:

import { visit } from '@ember/test-helpers';

export default async function visitWithAbortedTransition(url) {

  try {
    await visit(url);
  } catch(error) {
    const { message } = error;
    if (message !== 'TransitionAborted') {
      throw error;
    }
  }

};

@NullVoxPopuli
Copy link
Sponsor Collaborator

@ppcano that's pretty much what I'm doing now (for re-usability)

import { visit as dangerousVisit } from '@ember/test-helpers';

async function visit(url: string) {
  try {
    await dangerousVisit(url);
  } catch (e) {
    console.error(e);
  }
}

// tests omitted

I like your explicit checking for the TransitionAborted though

@tpetrone
Copy link

Also experiencing this when return transitionTo('_routeName_') inside beforeModel() hook.
New test suite. Ember 3.4.1

@chrisseto
Copy link

Minor addition to @ppcano's solution as the original settle is interrupted by the error.

import { settled, visit as _visit } from '@ember/test-helpers';

export async function visit(url: string) {
    try {
        await _visit(url);
    } catch (e) {
        if (e.message !== 'TransitionAborted') {
            throw e;
        }
    }

    await settled();
}

clekstro added a commit to travis-ci/travis-web that referenced this issue Jun 26, 2019
clekstro added a commit to travis-ci/travis-web that referenced this issue Jul 8, 2019
clekstro added a commit to travis-ci/travis-web that referenced this issue Jul 9, 2019
* Convert broadcasts test to new style

* Convert home/with-repositories to new testing style

* Fix ESLint error

* Convert  another test

* Silence ember-keyboard-shortcuts deprecation

* Convert owner not found test

* Update another test

* Convert enterprise banner test

* Convert enterprise/navigation test

* Ensure user is signed out in navigation test

This causes test flappiness when running all tests together

* Convert repo active on org test

* Convert automatic sign out test

This also includes a new helper (credit to @ppcano here: emberjs/ember-test-helpers#332 (comment)) to work around emberjs/ember-test-helpers#332.

* Fix ESLint errors

* Convert sign out test

* Convert feature flags test

* Convert pull requests test

* Convert builds restart test

* Convert builds view pull request

* Convert invalid builds test

* Convert build stages test

* Convert search flow test

* Convert profile not found test

* Convert profile redirect test

* Convert profile billing test (phew)

* Convert no search results test

* Convert user settings test

* Convert basic layout test

* Convert profile filtering test

* Conver view token test

* Convert test and remove unsightly unless/else

* Convert default layout test

* Convert header layout test

* Convert cta layout test

* Convert layout/plans test

* Convert job build matrix test

* Convert jobs/restart test

* Convert repo trigger build

* Convert invalid job log test

* Convert job test and fix resulting errors

* Convert job log error test

* Convert job delete test

* Convert job debug test

* Convert job cancel test

* Convert repo/not found test

* Convert repo/requests test

* Whoops missed the helper file

* Convert build list routes test

* Convert repo branches test

* Convert repo caches test

* Fix helper import in page object

* Convert repo show test

* Convert public pusher test

* More conversions

* Clear local storage between tests as well

* Missed part of the test

* Convert first sync test

* Add ember-lifeline

Hoping based on https://engineering.linkedin.com/blog/2018/01/ember-timer-leaks that it will help us identify some leakage that is causing some annoying test flappiness.

* Add data-test attrs to be able to use waitFor helper

* Convert sync test and remove unnecessary timeout/corresponding config

* Revert "Add ember-lifeline"

Was just an experiment, removing for now.

This reverts commit 9e434c4.

* Fix ESLint errors

* Convert skipped test (it is still skipped)

* Remove use of custom waitForElement helper

This is now built-in as `waitFor` within @ember/test-helpers.

* Convert environment instance initializer unit test

* Convert pro environment test and fix formatting in another test

* Import correct initialize function in test

* Skip sync tests temporarily

It appears that these tests are very flaky, so need to figure out a way to permanently fix them. I do not want them, however, to hold up this chunk of work.

* Remove legacy test helpers

* Add space for formatting

* Convert "unit" test to integration test

This does lose a single assertion that we are calling the auth service correctly, but I would argue that this should be a unit test on the auth service if it is needed at all.

* Fix ESLint errors

* Add comment regarding slow acceptance test

* Move final tests within module block

This was causing a super weird error regarding `window.wait`.

* Remove unnecessary import

We no longer use `withFeature` but rather import `enableFeature` directly in any test that needs it

* Remove logging statement from template

* Use our own setupApplicationTest helper

This will automatically sign a user out and clear browser state, ensuring every test starts off with the same state

* Remove unnecessary storage calls

This already happens within `setupApplicationTest`.

* Remove unnecessary use of signOutUser helper

I have left it in place for now (in case it has some use later), but we reset the local/session storage anyway before each acceptance test, and our only uses of it right now were essentially duplicating that work.
backspace added a commit to backspace/waydowntown-app that referenced this issue Nov 28, 2019
Thanks to @ppcano for the visitWithAbortedTransition suggestion here:
emberjs/ember-test-helpers#332
@velrest
Copy link

velrest commented May 6, 2020

Im still having this issue, in my case this happens if i add a async to my beforeModel hook. After that, every test which renders this route throws a TransitionAborted.

Copy link
Member

rwjblue commented May 6, 2020

What is in your beforeModel? Is it transitioning?

@netes
Copy link

netes commented May 6, 2020

Im still having this issue, in my case this happens if i add a async to my beforeModel hook. After that, every test which renders this route throws a TransitionAborted.

Make sure you're returning something from an asynchronous beforeModel, not just await.

@NullVoxPopuli
Copy link
Sponsor Collaborator

what would you return?

I regularly do this:

async beforeModel() {
  if (await some condition not met) {
    return this.transitionTo('do something');
  }
}

@velrest
Copy link

velrest commented May 11, 2020

Im returning nothing:

  async beforeModel() {
    if (logic) {
      await something;
    }
    if (!logic2) {
      this.replaceWith("/some/url");
    }
  }

Should i put a return before this.replaceWith? If yes, can someone explain to me why?

Copy link
Member

rwjblue commented May 12, 2020

Because otherwise, the beforeModel promise resolves before the replaceWith has resolved. This is commonly referred to as "floating promises" (@typescript-eslint/no-floating-promises), and will result in the AbortTransition rejection happening after the rest of the transition has moved further forward (and therefore will not be entangled with the router transition promise chain, which means the rejection happens out of band causing the reported error).

@velrest
Copy link

velrest commented May 14, 2020

@rwjblue Hmm in my failing test the problem occurs even with a return could there be another problem that im not aware of?

@velrest
Copy link

velrest commented May 14, 2020

Sadly i cant really debug it since the stack trace is just backburner calls. And stepping through the this.replaceWith was not as helpful as i thought.

@James1x0
Copy link

James1x0 commented May 14, 2020

@rwjblue We are running into this as well. The difference is that we are actually returning the abort. Any idea why this is still a problem based on the following code?

  async beforeModel () {
    const user = await this.auth.user,
          hints = user.get('shownHints');

    if (!hints || !hints.includes('documents')) {
      return this.transitionTo('account.documents.intro');
    }
  }

Previously user was a preset val, now it's a promise, so we have to await. Hence the reason we are "running into it".

@sethphillips
Copy link

just upgraded from 3.5.1 to 3.18.0 and am seeing this on any routes that we redirect in async beforeModel. similar to others, I'm doing something like this.

async beforeModel(){ let name = await fetch('athing') this.registration.setHostApp(name); return this.replaceWith('intended-route'); }

removing the async will remove the TransitionAborted error in tests.

@rwjblue
Copy link
Member

rwjblue commented Jun 5, 2020

Someone want to throw together a small repro repo, might be worth stepping through this again 🤔

@sethphillips
Copy link

Thanks for the fast response Robert.
https://github.com/sethphillips/transition-aborted-reproduction
great route works find and tests pass
borked route fails with TransitionAborted

@rwjblue
Copy link
Member

rwjblue commented Jun 7, 2020

Awesome, I'll try to poke at the repro this week. I remember looking through @simonihmig's initial repro, but things have changed enough upstream that it seems worth digging through again...

@patsy-issa
Copy link

Fwiw using redirect() to redirect instead of before/after/model does not cause visit to fail.

arnav28 added a commit to hashicorp/vault that referenced this issue Aug 18, 2021
- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332
arnav28 added a commit to hashicorp/vault that referenced this issue Aug 19, 2021
* Handle api explorer routing error

- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332

* Added changelog
arnav28 added a commit to hashicorp/vault that referenced this issue Aug 20, 2021
* Handle api explorer routing error

- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332

* Added changelog
arnav28 added a commit to hashicorp/vault that referenced this issue Aug 20, 2021
* Handle api explorer routing error

- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332

* Added changelog
@vfuto
Copy link

vfuto commented Aug 20, 2021

What if you invoke transitionTo from router service? this.transitionTo is deprecated.

@service router;
...
   this.router.transitionTo(route)
...

https://deprecations.emberjs.com/v3.x/#toc_routing-transition-methods

arnav28 added a commit to hashicorp/vault that referenced this issue Aug 20, 2021
* Handle api explorer routing error

- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332

* Added changelog
jartek pushed a commit to jartek/vault that referenced this issue Sep 11, 2021
* Handle api explorer routing error

- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332

* Added changelog
arnav28 added a commit to hashicorp/vault that referenced this issue Sep 15, 2021
* Handle api explorer routing error

- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332

* Added changelog
arnav28 added a commit to hashicorp/vault that referenced this issue Sep 16, 2021
* Handle api explorer routing error

- For some reason when routing is done during async process, router transtionTo throws the TransitionAbortedError
- As a fix treat this particular error as success since it doesn't interfere in the routing
- Reference: emberjs/ember-test-helpers#332

* Added changelog
@NullVoxPopuli
Copy link
Sponsor Collaborator

do we have a resolution here?

having visit throw on transition isn't good DX, imo.

I know fixing it would have to be a breaking release... but... that's fine? 🙃

@angelayanpan
Copy link

is this resolved?

@NullVoxPopuli
Copy link
Sponsor Collaborator

nope.

imo, someone should submit a PR that requires a breaking change of this library (major bump), and catches TransitionAborted errors.
Then, a new helper should be added, unsafeVisit that does what today's visit does

@angelayanpan
Copy link

this Issue is mentioned in 20 different places in our code. with ToDos

@elwayman02
Copy link
Contributor

this Issue is mentioned in 20 different places in our code. with ToDos

You're welcome 😂

@gnclmorais
Copy link

Riffing on @NullVoxPopuli, how about an extra key for the options the visit(…) method accepts? Something like { silenceTransitionAborted: true } or something like that… My thinking is that aborted transitions would not be the default and, with this extra option, it becomes explicit that we expect it. Thoughts?

@davideferre
Copy link

Is there any updates on this issue? In a test the only way to succede is to catch the exception generated by the visit() statement and check if the error message is TransitionAborted. It's a tricky way of testing and it can be misleading.
I also agree the idea by @gnclmorais

@fastfedora
Copy link

I discovered a new workaround after not being able to get the other workarounds mentioned above working in Ember 4.

I'm not entirely sure why this is working, but doing the transition within a function delayed via setTimeout fixes the error for me:

setTimeout(() => router.transitionTo(route), 0);

I think this is because it moves the entire transition out of the promise chain, so the rejection is not captured by ember-test-waiters. So this is still likely a hack and a workaround, rather than a permanent solution. But when I do this, the TransitionAborted rejection no longer appears (nor the subsequent testWaiter.endAsync called with no preceding testWaiter.beginAsync call. which appears in the console after the TransitionAborted rejection).

For context, the transition itself is happening within ember-simple-auth, which I've overridden to make the hack work. Currently we have an authenticated route that looks like:

export default class AuthenticatedRoute extends Route {
  @service session;
  ...

  async beforeModel(transition) {
    const loggedIn = this.session.requireAuthentication(transition, 'login');

    if (!loggedIn) {
      return;
    }

    ...
  }
}

The requireAuthentication eventually calls transitionTo when the user is not logged in, which is what generates the error. To fix this, I overrode requireAuthentication within the session service:

export default class AppSessionService extends SessionService {
  requireAuthentication(transition, routeOrCallback) {
    if (ENV.environment !== 'test' || typeof routeOrCallback !== 'string') {
      return super.requireAuthentication(transition, routeOrCallback);
    }

    const owner = getOwner(this);
    const router = owner.lookup('service:router') ?? owner.lookup('router:main');

    return super.requireAuthentication(
      transition,
      () => setTimeout(() => router.transitionTo(routeOrCallback), 0)
    );
  }
}

This works because the second parameter of requireAuthentication can be a route to redirect to or a callback to call when the user is not authenticated. We only ever pass a string route in, so wrapping it in a callback during testing to fix the error introduces minimal deviance from the production code.

@barryofguilder
Copy link

imo, someone should submit a PR that requires a breaking change of this library (major bump), and catches TransitionAborted errors. Then, a new helper should be added, unsafeVisit that does what today's visit does

I would love to have this update so we don't have to define our own to workaround this.

@NullVoxPopuli
Copy link
Sponsor Collaborator

If someone wants to do the Code/PR work, I can advocate for it to the ember core team and write up an RFC if needed, etc

davidtaylorhq added a commit to davidtaylorhq/router.js that referenced this issue Nov 16, 2023
The previous implementation of `followRedirects()` would catch a transition rejection and check the router for an `activeTransition`. This can become problematic in async situations because the destination transition may have resolved before the `reject` is scheduled on the previous transition. One such case is when redirecting from an async model hook to a destination route with synchronous model hooks.

This commit updates the `followRedirects()` logic to explicitly follow the redirect chain rather than relying on the presence of an `activeTransition`. This makes following redirects work correctly regardless of any scheduling concerns.

This problem has been noted in the context of the `visit()` test helper:

- emberjs/ember-test-helpers#332
- emberjs/ember.js#17150
@davidtaylorhq
Copy link

It looks like this issue extends beyond the visit() helper. It affects any use of transition.followRedirects() (which is essentially what visit() is doing).

I have a failing test case and fix for router.js here: tildeio/router.js#335

And then a tweak to Ember core so that it leans on followRedirects() instead of reimplementing the same logic: emberjs/ember.js#20574

davidtaylorhq added a commit to davidtaylorhq/router.js that referenced this issue Nov 16, 2023
The previous implementation of `followRedirects()` would catch a transition rejection and check the router for an `activeTransition`. This can become problematic in async situations because the destination transition may have resolved before the `reject` is scheduled on the previous transition. One such case is when redirecting from an async model hook to a destination route with synchronous model hooks.

This commit updates the `followRedirects()` logic to explicitly follow the redirect chain rather than relying on the presence of an `activeTransition`. This makes following redirects work correctly regardless of any scheduling concerns.

This problem has been noted in the context of the `visit()` test helper:

- emberjs/ember-test-helpers#332
- emberjs/ember.js#17150
simonihmig added a commit to simonihmig/ember-cli-page-object that referenced this issue Feb 28, 2024
This adds the actual error message from Ember's router to the (too generic) message from `visitable`. This is especially important when your test needs to know what kind of error was triggered. For example some tests might want to handle `TransitionAborted` errors differently, see emberjs/ember-test-helpers#332 (comment).
ro0gr added a commit to san650/ember-cli-page-object that referenced this issue Mar 13, 2024
* Better errors from visitable

This adds the actual error message from Ember's router to the (too generic) message from `visitable`. This is especially important when your test needs to know what kind of error was triggered. For example some tests might want to handle `TransitionAborted` errors differently, see emberjs/ember-test-helpers#332 (comment).

* Pass original error message from visit as Error#cause

* preserve original error instance

...under the `cause.error`

* doc: add a brief note about the `cause.error`

in the `visitable` error instance

---------

Co-authored-by: Ruslan Hrabovyi <kudgo.test@gmail.com>
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

No branches or pull requests