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

docs: update frontend testing section #474

Merged
merged 3 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 66 additions & 18 deletions docs/extend/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,25 +378,25 @@ When writing unit tests in Flarum, here are some helpful tips.
Unlike the running app, or even integration tests, there is no app/container/etc to inject service instances into our classes. Now all the useful settings, or helpers your extension use require a _mock_ . We want to limit mocking to just the key services, supporting only the minimum interactions needed to test the contract of our individual functions.

```php
public function setUp(): void
{
parent::setUp();
// example - if our setting needs settings, we can mock the settings repository
$settingsRepo = m::mock(SettingsRepositoryInterface::class);
// and then control specific return values for each setting key
$settingsRepo->shouldReceive('get')->with('some-plugin-key')->andReturn('some-value-useful-for-testing');
// construct your class under test, passing mocked services as needed
$this->serializer = new YourClassUnderTest($settingsRepo);
}
public function setUp(): void
{
parent::setUp();
// example - if our setting needs settings, we can mock the settings repository
$settingsRepo = m::mock(SettingsRepositoryInterface::class);
// and then control specific return values for each setting key
$settingsRepo->shouldReceive('get')->with('some-plugin-key')->andReturn('some-value-useful-for-testing');
// construct your class under test, passing mocked services as needed
$this->serializer = new YourClassUnderTest($settingsRepo);
}
```

Some aspects require more mocks. If you're validating authorization interactions for instance you might need to mock your users `User::class` and the request's method that provides them as well!

```
$this->actor = m::mock(User::class);
$request = m::mock(Request::class)->makePartial();
$request->shouldReceive('getAttribute->getActor')->andReturn($this->actor);
$this->actor->shouldReceive('SOME PERMISSION')->andReturn(true/false);
```php
$this->actor = m::mock(User::class);
$request = m::mock(Request::class)->makePartial();
$request->shouldReceive('getAttribute->getActor')->andReturn($this->actor);
$this->actor->shouldReceive('SOME PERMISSION')->andReturn(true/false);
```

NOTE: If you find your extension needs _lots and lots_ of mocks, or mocks that feel unrelated, it might be an opportunity to simplify your code, perhaps moving key logic into their own smaller functions (that dont require mocks).
Expand Down Expand Up @@ -504,11 +504,14 @@ Integration tests are used to test the components of your frontend code and the
Here's a simple example of an integration test for core's `Alert` component:

```ts
import bootstrapForum from '@flarum/jest-config/src/boostrap/forum';
import Alert from '../../../../src/common/components/Alert';
import m from 'mithril';
import mq from 'mithril-query';
import { jest } from '@jest/globals';

beforeAll(() => bootstrapForum());

describe('Alert displays as expected', () => {
it('should display alert messages with an icon', () => {
const alert = mq(m(Alert, { type: 'error' }, 'Shoot!'));
Expand Down Expand Up @@ -555,15 +558,60 @@ describe('Alert is dismissible', () => {
});
```

#### Methods
#### Custom Matchers

Apart from the [available Jest matchers](https://jestjs.io/docs/expect), these are the custom methods that are available for mithril component tests:

These are the custom methods that are available for mithril component tests:
* **`toHaveElement(selector)`** - Checks if the component has an element that matches the given selector.
* **`toHaveElementAttr(selector, attribute, value)`** - Checks if the component has an element that matches the given selector with the given attribute and value.
* **`toContainRaw(content)`** - Checks if the component HTML contains the given content.

To negate any of these methods, simply prefix them with `not.`. For example, `expect(alert).not.toHaveElement('button.Alert-dismiss');`. For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests.
To negate any of these methods, simply prefix them with `not.`. For example:

```ts
expect(alert).not.toHaveElement('button.Alert-dismiss');
```

For more information, check out the [Jest docs](https://jestjs.io/docs/using-matchers). For example you may need to check how to [mock functions](https://jestjs.io/docs/mock-functions), or how to use `beforeEach` and `afterEach` to set up and tear down tests.

### Bootstrapping the flarum app

Depending on the test you are writing, you may need to bootstrap the Flarum app. This is done by calling either `bootstrapForum()` or `bootstrapAdmin()` from `@flarum/jest-config/src/bootstrap`. This will initialize the global Flarum app object for you to use in your tests.

You cannot bootstrap both the forum and admin app in the same test file. If you need to test both, you will need to split your tests into separate files.

###### Examples

```ts
import bootstrapForum from '@flarum/jest-config/src/boostrap/forum';

describe('Forum tests', () => {
beforeAll(() => bootstrapForum());

it('should do something', () => {
// your test code here
});
});
```

```ts
import bootstrapAdmin from '@flarum/jest-config/src/boostrap/admin';

describe('Admin tests', () => {
beforeAll(() => bootstrapAdmin());

it('should do something', () => {
// your test code here
});
});
```

:::tip

Checkout the Flarum core tests for more examples on how to write tests for your extension:
https://github.com/flarum/framework/tree/2.x/framework/core/js/tests

:::

## E2E Tests

Expand Down
12 changes: 10 additions & 2 deletions src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ li > .menu__list {
left: 0;
top: 0;
height: 100%;
width: calc(var(--doc-sidebar-width) + (100vw - 1800px)/2 - 8px);
width: max(var(--doc-sidebar-width), calc(var(--doc-sidebar-width) + (100vw - 1800px)/2 - 8px));
background: var(--ifm-color-secondary);
}

Expand All @@ -218,7 +218,15 @@ h1 > .hash-link, h2 > .hash-link, h3 > .hash-link, h4 > .hash-link, h5 > .hash-l

.notable {
color: #3e3400;
--bg: #fff2ae;
--bg: #ebd022;
}

[data-theme=light] .breaking {
color: #ffefef;
}

[data-theme=light] .notable {
--bg: #ebd022;
}

.breaking, .notable {
Expand Down