aurelia / aurelia/testing

Tests using ComponentTester.manuallyHandleLifecycle() can cause unrelated tests to fail

Open
#95 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
38
Forks
27
PR merge metrics
No merged PRs in 30d

Description

**I'm submitting a bug report**

* **Library Version:**
1.0.0

**Please tell us about your environment:**
* **Operating System:**
Windows 10

* **Node Version:**
10.16.0

* **NPM Version:**
6.9.0

* **JSPM OR Webpack AND Version**
JSPM 0.16.54

* **Browser:**
Chrome 78

* **Language:**
ESNext

**Current behavior:**
When using ComponentTester with `.manuallyHandleLifecycle()` (as shown [in the documentation](https://aurelia.io/docs/testing/components#manually-handling-lifecycle)), if the test does not call `.bind()` or `.attached()`, it can cause failures for subsequent tests. This seems to be caused by ComponentTester's [_prepareLifecycle()](https://github.com/aurelia/testing/blob/master/src/component-tester.ts#L102) method, which alters the View prototype's `.bind` and `.attached` properties. If the `.bind` and `.attached` methods of ComponentTester are not called, View.prototype will never be restored to its original behavior, causing subsequent tests to fail if they depend on the `.bind` and `.attached` methods of View.

***Steps to reproduce***
1. Create my-component.js and my-component.html
```javascript
//my-component.js
export class MyComponentCustomElement {
constructor() {
this.myProperty = "created";
}

bind() {
this.myProperty = "bound";
}

attached() {
this.myProperty = "attached";
}
}
```
```html

```
2. Create a test suite for `my-component` (I'm using Jasmine here)
```javascript
//my-component.spec.js
import { StageComponent } from 'aurelia-testing';
import { bootstrap } from 'aurelia-bootstrapper';

describe('My Component', () => {
let componentTester;

beforeEach(() => {
componentTester = StageComponent
.withResources('my-component')
.inView('')
.boundTo({});
});

afterEach(() => {
componentTester.dispose();
});

it('should manually handle partial lifecycles', done => {
componentTester.manuallyHandleLifecycle().create(bootstrap)
.then(() => {
expect(componentTester.viewModel.myProperty).toBe('created');
})
.then(() => componentTester.bind())
.then(() => {
expect(componentTester.viewModel.myProperty).toBe('bound');
})
.then(done)
.catch(reason => {
fail(reason);
done();
});
});

it('should manually handle full lifecycles', done => {
componentTester.manuallyHandleLifecycle().create(bootstrap)
.then(() => componentTester.bind())
.then(() => componentTester.attached())
.then(() => {
expect(componentTester.viewModel.myProperty).toBe("attached");
})
.then(done)
.catch(reason => {
fail(reason);
done();
});
});
});
```
3. Run the tests. The first test will pass and the second test will fail. If the test runner executes tests in a random order, and the second test is executed first, both tests will pass.

This behavior is also observed when a lifecycle Promise is rejected, as this causes the rest of the Promise chain to be skipped.

**Expected/desired behavior:**

Both tests should pass, regardless of their execution order. When ComponentTester.dispose() is called, I expect all changes it made to the environment to be reset. I expect View objects to have their original function. Additionally, when a test fails due to a rejected Promise, I expect other tests to be unaffected.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.