Tests using ComponentTester.manuallyHandleLifecycle() can cause unrelated tests to fail
- 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
Assessment
This issue has not been assessed yet.