Skip to content

Commit

Permalink
fix: make <Link> inert for setupRenderingTest
Browse files Browse the repository at this point in the history
Fixes #126.
  • Loading branch information
buschtoens committed Aug 28, 2019
1 parent 0b4ea4c commit 2d21117
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
30 changes: 30 additions & 0 deletions addon/components/link/component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { assert } from '@ember/debug';
import { action } from '@ember/object';
import { reads } from '@ember/object/computed';
import Transition from '@ember/routing/-private/transition';
Expand Down Expand Up @@ -57,6 +58,17 @@ export default class LinkComponent extends Component<LinkArgs> {
@service
private router!: RouterService;

/**
* Whether the router has been initialized. This will be false in render
* tests.
*
* @see https://github.com/buschtoens/ember-link/issues/126
*/
private get isRouterInitialized() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return Boolean((this.router as any)._router._routerMicrolib);
}

@reads('router.currentURL')
private currentURL!: string;

Expand Down Expand Up @@ -97,6 +109,7 @@ export default class LinkComponent extends Component<LinkArgs> {
* attribute.
*/
get href(): string {
if (!this.isRouterInitialized) return '';
return this.router.urlFor(...this.routeArgs);
}

Expand All @@ -105,6 +118,7 @@ export default class LinkComponent extends Component<LinkArgs> {
* models and query params.
*/
get isActive(): boolean {
if (!this.isRouterInitialized) return false;
this.currentURL; // eslint-disable-line no-unused-expressions
return this.router.isActive(...this.routeArgs);
}
Expand All @@ -114,6 +128,7 @@ export default class LinkComponent extends Component<LinkArgs> {
* models, but ignoring query params.
*/
get isActiveWithoutQueryParams() {
if (!this.isRouterInitialized) return false;
this.currentURL; // eslint-disable-line no-unused-expressions
return this.router.isActive(
this.args.route,
Expand All @@ -130,6 +145,7 @@ export default class LinkComponent extends Component<LinkArgs> {
* params.
*/
get isActiveWithoutModels() {
if (!this.isRouterInitialized) return false;
this.currentURL; // eslint-disable-line no-unused-expressions
return this.router.isActive(this.args.route);
}
Expand All @@ -139,8 +155,15 @@ export default class LinkComponent extends Component<LinkArgs> {
*/
@action
transitionTo(event?: Event | unknown): Transition {
// Intentionally putting this *before* the assertion to prevent navigating
// away in case of a failed assertion.
this.preventDefault(event);

assert(
'You can only call `transitionTo`, when the router is initialized, e.g. when using `setupApplicationTest`.',
this.isRouterInitialized
);

return this.router.transitionTo(...this.routeArgs);
}

Expand All @@ -150,8 +173,15 @@ export default class LinkComponent extends Component<LinkArgs> {
*/
@action
replaceWith(event?: Event | unknown): Transition {
// Intentionally putting this *before* the assertion to prevent navigating
// away in case of a failed assertion.
this.preventDefault(event);

assert(
'You can only call `replaceWith`, when the router is initialized, e.g. when using `setupApplicationTest`.',
this.isRouterInitialized
);

return this.router.replaceWith(...this.routeArgs);
}

Expand Down
47 changes: 47 additions & 0 deletions tests/helpers/wait-for-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { waitUntil } from '@ember/test-helpers';

import Ember from 'ember';

import { on, off } from 'rsvp';

/**
* I would be using `ember-qunit-assert-helpers` here, but it does not work with
* async rendering. :(
*
* Adapted from https://github.com/workmanw/ember-qunit-assert-helpers/issues/18#issuecomment-390003905
*/
export default async function waitForError(
callback: () => Promise<unknown>,
options?: Parameters<typeof waitUntil>[1]
) {
const originalEmberListener = Ember.onerror;
const originalWindowListener = window.onerror;

let error: Error | undefined;
Ember.onerror = uncaughtError => {
error = uncaughtError;
};
window.onerror = (
_message,
_source,
_lineNumber,
_columnNumber,
uncaughtError
) => {
error = uncaughtError;
};
on('error', Ember.onerror);

await Promise.all([
waitUntil(() => error, options).finally(() => {
Ember.onerror = originalEmberListener;
window.onerror = originalWindowListener;
off('error', Ember.onerror);
}),
callback()
]);

if (!error) throw new Error('No Error was thrown.');

return error;
}
52 changes: 52 additions & 0 deletions tests/integration/components/link-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { render, click } from '@ember/test-helpers';
import { setupRenderingTest } from 'ember-qunit';
import { module, test } from 'qunit';

import hbs from 'htmlbars-inline-precompile';

import waitForError from 'dummy/tests/helpers/wait-for-error';

module('Integration | Component | link', function(hooks) {
setupRenderingTest(hooks);

// Regression for: https://github.com/buschtoens/ember-link/issues/126
test('it renders', async function(assert) {
await render(hbs`
<Link @route="foo" as |l|>
<a
data-test-link
href={{l.href}}
class={{if l.isActive "is-active"}}
{{on "click" l.transitionTo}}
>
Link
</a>
</Link>
`);

assert.dom('[data-test-link]').hasAttribute('href', '');
assert.dom('[data-test-link]').hasNoClass('is-active');
});

test('triggering a transition has no effect', async function(assert) {
await render(hbs`
<Link @route="foo" as |l|>
<a
data-test-link
href={{l.href}}
class={{if l.isActive "is-active"}}
{{on "click" l.transitionTo}}
>
Link
</a>
</Link>
`);

const error = await waitForError(() => click('[data-test-link]'));
assert.ok(error instanceof Error);
assert.strictEqual(
error.message,
'Assertion Failed: You can only call `transitionTo`, when the router is initialized, e.g. when using `setupApplicationTest`.'
);
});
});

0 comments on commit 2d21117

Please sign in to comment.