{"id":13495595,"url":"https://github.com/ngneat/spectator","last_synced_at":"2025-05-12T13:11:18.709Z","repository":{"id":37405696,"uuid":"102265558","full_name":"ngneat/spectator","owner":"ngneat","description":"🦊 🚀 A Powerful Tool to Simplify Your Angular Tests","archived":false,"fork":false,"pushed_at":"2025-04-29T18:24:53.000Z","size":12809,"stargazers_count":2150,"open_issues_count":63,"forks_count":178,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-05-04T10:34:54.112Z","etag":null,"topics":["angular","angular-testing","clean","easy","testing","typescript","unittest"],"latest_commit_sha":null,"homepage":"https://ngneat.github.io/spectator","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ngneat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"ngneat"}},"created_at":"2017-09-03T13:11:55.000Z","updated_at":"2025-05-03T13:16:11.000Z","dependencies_parsed_at":"2023-02-10T01:30:58.920Z","dependency_job_id":"70c89a4d-cae0-4c6d-8da6-d8b2191b091f","html_url":"https://github.com/ngneat/spectator","commit_stats":{"total_commits":756,"total_committers":113,"mean_commits":"6.6902654867256635","dds":0.626984126984127,"last_synced_commit":"618c2d07ea8481c66b30794a8df9f2d3c94643f0"},"previous_names":["netanelbasal/ngx-easy-test","netanelbasal/spectator"],"tags_count":183,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fspectator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fspectator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fspectator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Fspectator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ngneat","download_url":"https://codeload.github.com/ngneat/spectator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253745166,"owners_count":21957317,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angular-testing","clean","easy","testing","typescript","unittest"],"created_at":"2024-07-31T19:01:36.262Z","updated_at":"2025-05-12T13:11:18.665Z","avatar_url":"https://github.com/ngneat.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n \u003cimg width=\"100%\" height=\"100%\" src=\"https://raw.githubusercontent.com/ngneat/spectator/master/image.svg?sanitize=true\"\u003e\n\u003c/p\u003e\n\n[![All Contributors](https://img.shields.io/badge/all_contributors-48-orange.svg?style=flat-square)](#contributors)\n[![spectator](https://img.shields.io/badge/tested%20with-spectator-2196F3.svg?style=flat-square)]()\n[![MIT](https://img.shields.io/packagist/l/doctrine/orm.svg?style=flat-square)]()\n[![commitizen](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)]()\n[![PRs](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)]()\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/ngneat/spectator/ci.yml?branch=master\u0026style=flat-square\n)](https://github.com/ngneat/spectator/actions/workflows/ci.yml?query=branch%3Amaster)\n\n\u003e A Powerful Tool to Simplify Your Angular Tests\n\nSpectator helps you get rid of all the boilerplate grunt work, leaving you with readable, sleek and streamlined unit tests.\n\n## Features\n- ✅ Support for testing Angular components, directives and services\n- ✅ Easy DOM querying\n- ✅ Clean API for triggering keyboard/mouse/touch events\n- ✅ Testing `ng-content`\n- ✅ Custom Jasmine/Jest/Vitest Matchers (toHaveClass, toBeDisabled..)\n- ✅ Routing testing support\n- ✅ HTTP testing support\n- ✅ Built-in support for entry components\n- ✅ Built-in support for component providers\n- ✅ Auto-mocking providers\n- ✅ Strongly typed\n- ✅ Jest Support\n- ✅ Vitest Support\n\n\n## Sponsoring ngneat\n\n[Sponsorships](https://github.com/sponsors/ngneat) aid in the continued development and maintenance of ngneat libraries. Consider asking your company to sponsor ngneat as its core to their business and application development.\n\n### Gold Sponsors\n\nElevate your support by becoming a Gold Sponsor and have your logo prominently featured on our README in the top 5 repositories.\n\n### Silver Sponsors\n\nBoost your backing by becoming a Gold Sponsor and enjoy the spotlight with your logo prominently showcased in the top 3 repositories on our README.\n\n### Bronze Sponsors\n\n\u003ca href=\"https://houseofangular.io\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://github.com/ngrx/platform/blob/main/projects/ngrx.io/src/assets/images/sponsors/house-of-angular.png\" width=\"50px\" height=\"50px\" alt=\"House of Angular\" /\u003e\n\u003c/a\u003e\n\nBecome a bronze sponsor and get your logo on our README on GitHub.\n\n\n\n## Table of Contents\n\n- [Features](#features)\n- [Sponsoring ngneat](#sponsoring-ngneat)\n  - [Gold Sponsors](#gold-sponsors)\n  - [Silver Sponsors](#silver-sponsors)\n  - [Bronze Sponsors](#bronze-sponsors)\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n  - [NPM](#npm)\n  - [Yarn](#yarn)\n- [Testing Components](#testing-components)\n  - [Events API](#events-api)\n    - [Custom Events](#custom-events)\n    - [Event Creators](#event-creators)\n  - [Keyboard helpers](#keyboard-helpers)\n  - [Mouse helpers](#mouse-helpers)\n  - [Queries](#queries)\n    - [String Selector](#string-selector)\n    - [Type Selector](#type-selector)\n    - [DOM Selector](#dom-selector)\n  - [Parent Selector](#parent-selector)\n    - [Testing Select Elements](#testing-select-elements)\n    - [Mocking Components](#mocking-components)\n    - [Testing Single Component/Directive Angular Modules](#testing-single-componentdirective-angular-modules)\n  - [Deferrable Views](#deferrable-views)\n    - [Nested Deferrable Views](#nested-deferrable-views)\n- [Testing with Host](#testing-with-host)\n  - [Custom Host Component](#custom-host-component)\n- [Testing with Routing](#testing-with-routing)\n  - [Triggering a navigation](#triggering-a-navigation)\n  - [Integration testing with `RouterTestingModule`](#integration-testing-with-routertestingmodule)\n  - [Routing Options](#routing-options)\n- [Testing Directives](#testing-directives)\n- [Testing Services](#testing-services)\n  - [Additional Options](#additional-options)\n- [Testing Pipes](#testing-pipes)\n  - [Using Custom Host Component](#using-custom-host-component)\n- [Testing DI Functions](#testing-di-functions)\n- [Mocking Providers](#mocking-providers)\n  - [Mocking OnInit dependencies](#mocking-oninit-dependencies)\n  - [Mocking constructor dependencies](#mocking-constructor-dependencies)\n- [Jest Support](#jest-support)\n- [Vitest Support](#vitest-support)\n- [Testing with HTTP](#testing-with-http)\n- [Global Injections](#global-injections)\n- [Component Providers](#component-providers)\n- [Custom Matchers](#custom-matchers)\n- [Schematics](#schematics)\n- [Default Schematics Collection](#default-schematics-collection)\n- [Working Spectator \\\u0026 Jest Sample Repo and Karma Comparison](#working-spectator--jest-sample-repo-and-karma-comparison)\n- [Core Team](#core-team)\n- [Contributors](#contributors)\n\n## Installation\n\n### NPM\n\n`npm install @ngneat/spectator --save-dev`\n\n### Yarn\n\n`yarn add @ngneat/spectator --dev`\n\n## Testing Components\nCreate a component factory by using the `createComponentFactory()` function, passing the component class that you want to test.\nThe `createComponentFactory()` returns a function that will create a fresh component in each `it` block:\n\n```ts\nimport { Spectator, createComponentFactory } from '@ngneat/spectator';\nimport { ButtonComponent } from './button.component';\n\ndescribe('ButtonComponent', () =\u003e {\n  let spectator: Spectator\u003cButtonComponent\u003e;\n  const createComponent = createComponentFactory(ButtonComponent);\n\n  beforeEach(() =\u003e spectator = createComponent());\n\n  it('should have a success class by default', () =\u003e {\n    expect(spectator.query('button')).toHaveClass('success');\n  });\n\n  it('should set the class name according to the [className] input', () =\u003e {\n    spectator.setInput('className', 'danger');\n    expect(spectator.query('button')).toHaveClass('danger');\n    expect(spectator.query('button')).not.toHaveClass('success');\n  });\n});\n```\n\nThe `createComponentFactory` function can optionally take the following options which extends the basic Angular Testing Module options:\n\n```ts\nconst createComponent = createComponentFactory({\n  component: ButtonComponent,\n  imports: [],\n  providers: [],\n  declarations: [],\n  entryComponents: [],\n  componentProviders: [], // Override the component's providers\n  componentViewProviders: [], // Override the component's view providers\n  componentImports: [], // Override the component's imports in case of testing standalone component\n  overrideModules: [], // Override modules\n  overrideComponents: [], // Override components in case of testing standalone component\n  overrideDirectives: [], // Override directives in case of testing standalone directive\n  overridePipes: [], // Override pipes in case of testing standalone pipe\n  mocks: [], // Providers that will automatically be mocked\n  componentMocks: [], // Component providers that will automatically be mocked\n  componentViewProvidersMocks: [], // Component view providers that will be automatically mocked\n  detectChanges: false, // Defaults to true\n  declareComponent: false, // Defaults to true\n  disableAnimations: false, // Defaults to true\n  shallow: true, // Defaults to false\n  deferBlockBehavior: DeferBlockBehavior // Defaults to DeferBlockBehavior.Playthrough\n});\n```\n\nThe `createComponent()` function optionally takes the following options:\n```ts\nit('should...', () =\u003e {\n  spectator = createComponent({\n    // The component inputs\n    props: {\n      title: 'Click'\n    },\n    // Override the component's providers\n    // Note that you must declare it once in `createComponentFactory`\n    providers: [],\n    // Whether to run change detection (defaults to true)\n    detectChanges: false\n  });\n\n  expect(spectator.query('button')).toHaveText('Click');\n});\n```\n\nBy providing `overrideComponents` options in scope of our `createComponent()` function we can define the way of overriding standalone component and it's dependencies\n```ts\n@Component({\n  selector: `app-standalone-with-import`,\n  template: `\u003cdiv id=\"standalone\"\u003eStandalone component with import!\u003c/div\u003e\n  \u003capp-standalone-with-dependency\u003e\u003c/app-standalone-with-dependency\u003e`,\n  imports: [StandaloneComponentWithDependency],\n  standalone: true,\n})\nexport class StandaloneWithImportsComponent {}\n\n@Component({\n  selector: `app-standalone-with-dependency`,\n  template: `\u003cdiv id=\"standaloneWithDependency\"\u003eStandalone component with dependency!\u003c/div\u003e`,\n  standalone: true,\n})\nexport class StandaloneComponentWithDependency {\n  constructor(public query: QueryService) {}\n}\n\n@Component({\n  selector: `app-standalone-with-dependency`,\n  template: `\u003cdiv id=\"standaloneWithDependency\"\u003eStandalone component with override dependency!\u003c/div\u003e`,\n  standalone: true,\n})\nexport class MockStandaloneComponentWithDependency {\n  constructor() {}\n}\n\nit('should...', () =\u003e {\n  const spectator = createHostFactory({\n    component: StandaloneWithImportsComponent,\n    template: `\u003cdiv\u003e\u003capp-standalone-with-import\u003e\u003c/app-standalone-with-import\u003e\u003c/div\u003e`,\n    overrideComponents: [\n      [\n        StandaloneWithImportsComponent,\n        {\n          remove: { imports: [StandaloneComponentWithDependency] },\n          add: { imports: [MockStandaloneComponentWithDependency] },\n        },\n      ],\n    ],\n  });\n\n  expect(host.query('#standalone')).toContainText('Standalone component with import!');\n  expect(host.query('#standaloneWithDependency')).toContainText('Standalone component with override dependency!');\n});\n```\n\nBy passing `componentImports` config to our `createComponent()` function we can remove and override imports directly in the tested standalone component. The `componentImports` property accepts an array of overrides. An override is an array of length 2, where the first entry is the original import to be removed from the tested component during the test and the second entry is the replacement import. In the example below, `StandaloneComponentWithDependency` is removed from the tested component, and `MockStandaloneComponentWithDependency` is added to the tested component imports.\n```ts\n@Component({\n  selector: `app-standalone-with-import`,\n  template: `\u003cdiv id=\"standalone\"\u003eStandalone component with import!\u003c/div\u003e\n  \u003capp-standalone-with-dependency\u003e\u003c/app-standalone-with-dependency\u003e`,\n  imports: [StandaloneComponentWithDependency],\n  standalone: true,\n})\nexport class StandaloneWithImportsComponent {}\n\n@Component({\n  selector: `app-standalone-with-dependency`,\n  template: `\u003cdiv id=\"standaloneWithDependency\"\u003eStandalone component with dependency!\u003c/div\u003e`,\n  standalone: true,\n})\nexport class StandaloneComponentWithDependency {\n  constructor(public query: QueryService) {}\n}\n\n@Component({\n  selector: `app-standalone-with-dependency`,\n  template: `\u003cdiv id=\"standaloneWithDependency\"\u003eStandalone component with override dependency!\u003c/div\u003e`,\n  standalone: true,\n})\nexport class MockStandaloneComponentWithDependency {\n  constructor() {}\n}\n\nit('should...', () =\u003e {\n  const spectator = createHostFactory({\n    component: StandaloneWithImportsComponent,\n    template: `\u003cdiv\u003e\u003capp-standalone-with-import\u003e\u003c/app-standalone-with-import\u003e\u003c/div\u003e`,\n    componentImports: [\n      [\n        StandaloneComponentWithDependency,\n        MockStandaloneComponentWithDependency,\n      ],\n    ],\n  });\n\n  expect(host.query('#standalone')).toContainText('Standalone component with import!');\n  expect(host.query('#standaloneWithDependency')).toContainText('Standalone component with override dependency!');\n});\n```\n\nThe `createComponent()` method returns an instance of `Spectator` which exposes the following API:\n\n- `fixture` - The tested component's fixture\n- `component` - The tested component's instance\n- `element` - The tested component's native element\n- `debugElement` - The tested fixture's debug element\n\n- `flushEffects()` - Provides a wrapper for `TestBed.flushEffects()`\n- `runInInjectionContext()` - Provides a wrapper for `TestBed.runInInjectionContext()`\n- `inject()` - Provides a wrapper for `TestBed.inject()`:\n```ts\nconst service = spectator.inject(QueryService);\n\nconst fromComponentInjector = true;\nconst service = spectator.inject(QueryService, fromComponentInjector);\n```\n- `detectChanges()` - Runs detectChanges on the tested element/host:\n```ts\nspectator.detectChanges();\n```\n\n- `detectComponentChanges()` - Runs `detectChanges` on the **tested** component ( not on the `host` ).\n  You'll need this method in __rare__ cases when using a `host` and the tested component is `onPush`, and you want to force it to run a change detection cycle.\n\n```ts\nspectator.detectComponentChanges();\n```\n\n- `setInput()` - Changes the value of an @Input() of the tested component.\n  Method runs `ngOnChanges` with `SimpleChanges` manually if it exists.\n```ts\nit('should...', () =\u003e {\n  spectator.setInput('className', 'danger');\n\n  spectator.setInput({\n    className: 'danger'\n  });\n});\n```\n- `output` - Returns an Observable @Output() of the tested component:\n```ts\nit('should emit the $event on click', () =\u003e {\n  let output;\n  spectator.output('click').subscribe(result =\u003e (output = result));\n\n  spectator.component.onClick({ type: 'click' });\n  expect(output).toEqual({ type: 'click' });\n});\n```\n- `tick(millis?: number)` - Run the fakeAsync `tick()` function and call `detectChanges()`:\n```ts\nit('should work with tick', fakeAsync(() =\u003e {\n  spectator = createComponent(ZippyComponent);\n  spectator.component.update();\n  expect(spectator.component.updatedAsync).toBeFalsy();\n  spectator.tick(6000);\n  expect(spectator.component.updatedAsync).not.toBeFalsy();\n}))\n```\n\n### Events API\nEach one of the events can accept a `SpectatorElement` which can be one of the following:\n\n```ts\ntype SpectatorElement = string | Element | DebugElement | ElementRef | Window | Document | DOMSelector;\n```\n\nIf not provided, the default element will be the host element of the component under test.\n\n- `click()` - Triggers a click event:\n```ts\nspectator.click(SpectatorElement);\nspectator.click(byText('Element'));\n```\n- `blur()` - Triggers a blur event:\n```ts\nspectator.blur(SpectatorElement);\nspectator.blur(byText('Element'));\n```\nNote that if using the jest framework, blur() only works if the element is focused. [Details](https://github.com/ngneat/spectator/issues/373#issuecomment-896285805).\n- `focus()` - Triggers a focus event:\n```ts\nspectator.focus(SpectatorElement);\nspectator.focus(byText('Element'));\n```\n- `typeInElement()` - Simulating the user typing:\n```ts\nspectator.typeInElement(value, SpectatorElement);\nspectator.typeInElement(value, byText('Element'));\n```\n- `dispatchMouseEvent()` - Triggers a mouse event:\n```ts\nspectator.dispatchMouseEvent(SpectatorElement, 'mouseout');\nspectator.dispatchMouseEvent(SpectatorElement, 'mouseout'), x, y, event);\nspectator.dispatchMouseEvent(byText('Element'), 'mouseout');\nspectator.dispatchMouseEvent(byText('Element'), 'mouseout', x, y, event);\n```\n- `dispatchKeyboardEvent()` - Triggers a keyboard event:\n```ts\nspectator.dispatchKeyboardEvent(SpectatorElement, 'keyup', 'Escape');\nspectator.dispatchKeyboardEvent(SpectatorElement, 'keyup', { key: 'Escape', keyCode: 27 })\nspectator.dispatchKeyboardEvent(byText('Element'), 'keyup', 'Escape');\nspectator.dispatchKeyboardEvent(byText('Element'), 'keyup', { key: 'Escape', keyCode: 27 })\n```\n- `dispatchTouchEvent()` - Triggers a touch event:\n```ts\nspectator.dispatchTouchEvent(SpectatorElement, type, x, y);\nspectator.dispatchTouchEvent(byText('Element'), type, x, y);\n```\n\n#### Custom Events\n\nYou can trigger custom events (@Output() of child components) [using](https://github.com/ngneat/spectator/blob/master/projects/spectator/test/child-custom-event/child-custom-event-parent.component.spec.ts) the following method:\n```ts\nspectator.triggerEventHandler(MyChildComponent, 'myCustomEvent', 'eventValue');\nspectator.triggerEventHandler(MyChildComponent, 'myCustomEvent', 'eventValue', { root: true});\n\nspectator.triggerEventHandler('app-child-component', 'myCustomEvent', 'eventValue');\nspectator.triggerEventHandler('app-child-component', 'myCustomEvent', 'eventValue', { root: true});\n\n```\n\n#### Event Creators\n\nIn case you want to test events independently of any template (e.g. in presenter services) you can fallback on the underlying event creators.\nThey are basically providing the same signature without the preceding element.\n```ts\nconst keyboardEvent = createKeyboardEvent('keyup', 'ArrowDown'/*, targetElement */);\nconst mouseEvent = createMouseEvent('mouseout');\nconst touchEvent = createTouchEvent('touchmove');\nconst fakeEvent = createFakeEvent('input');\n```\n\n### Keyboard helpers\n```ts\nspectator.keyboard.pressEnter();\nspectator.keyboard.pressEscape();\nspectator.keyboard.pressTab();\nspectator.keyboard.pressBackspace();\nspectator.keyboard.pressKey('a');\nspectator.keyboard.pressKey('ctrl.a');\nspectator.keyboard.pressKey('ctrl.shift.a');\n```\n\n### Mouse helpers\n```ts\nspectator.mouse.contextmenu('.selector');\nspectator.mouse.dblclick('.selector');\n```\n\nNote that each one of the above methods will also run `detectChanges()`.\n\n### Queries\nThe Spectator API includes convenient methods for querying the DOM as part of a test: `query`, `queryAll`, `queryLast` , `queryHost` and `queryHostAll`. All query methods are polymorphic and allow you to query using any of the following techniques.\n\n#### String Selector\n Pass a string selector (in the same style as you would when using jQuery or document.querySelector) to query for elements that match that path in the DOM. This method for querying is equivalent to Angular's By.css predicate. Note that native HTML elements will be returned. For example:\n ```ts\n// Returns a single HTMLElement\nspectator.query('div \u003e ul.nav li:first-child');\n// Returns an array of all matching HTMLElements\nspectator.queryAll('div \u003e ul.nav li');\n\n// Query from the document context\nspectator.query('div', { root: true });\n\nspectator.query('app-child', { read: ChildServiceService });\n```\n#### Type Selector\nPass a type (such as a component, directive or provider class) to query for instances of that type in the DOM. This is equivalent to Angular's `By.directive` predicate. You can optionally pass in a second parameter to read a specific injection token from the matching elements' injectors. For example:\n```ts\n// Returns a single instance of MyComponent (if present)\nspectator.query(MyComponent);\n\n// Returns the instance of `SomeService` found in the instance of `MyComponent` that exists in the DOM (if present)\nspectator.query(MyComponent, { read: SomeService });\n\nspectator.query(MyComponent, { read: ElementRef });\nhost.queryLast(ChildComponent);\nhost.queryAll(ChildComponent);\n```\n#### DOM Selector\nSpectator allows you to query for elements using selectors inspired by [dom-testing-library](https://testing-library.com/docs/dom-testing-library/api-queries). The available selectors are:\n\n```ts\nspectator.query(byPlaceholder('Please enter your email address'));\nspectator.query(byValue('By value'));\nspectator.query(byTitle('By title'));\nspectator.query(byAltText('By alt text'));\nspectator.query(byLabel('By label'));\nspectator.query(byText('By text'));\nspectator.query(byText('By text', {selector: '#some .selector'}));\nspectator.query(byTextContent('By text content', {selector: '#some .selector'}));\nspectator.query(byRole('checkbox', { checked: true }));\n```\n\nThe difference between `byText` and `byTextContent` is that the former doesn't match text inside a nested elements.\n\nFor example, in this following HTML `byText('foobar', {selector: 'div'})` won't match the following `div`, but `byTextContent` will:\n```html\n\u003cdiv\u003e\n  \u003cspan\u003efoo\u003c/span\u003e\n  \u003cspan\u003ebar\u003c/span\u003e\n\u003c/div\u003e\n```\n\n### Parent Selector\nSpectator allows you to query for nested elements within a parent element. This is useful when you have multiple instances of the same component on the page and you want to query for children within a specific one. The parent selector is a string selector that is used to find the parent element. The parent selector is passed as the second parameter to the query methods. For example:\n```ts\nspectator.query(ChildComponent, { parentSelector: '#parent-component-1' });\nspectator.queryAll(ChildComponent, { parentSelector: '#parent-component-1' });\n```\n\n#### Testing Select Elements\nSpectator allows you to test `\u003cselect\u003e\u003c/select\u003e` elements easily, and supports multi select.\n\nExample:\n```ts\nit('should set the correct options on multi select', () =\u003e {\n  const select = spectator.query('#test-multi-select') as HTMLSelectElement;\n  spectator.selectOption(select, ['1', '2']);\n  expect(select).toHaveSelectedOptions(['1', '2']);\n});\n\nit('should set the correct option on standard select', () =\u003e {\n  const select = spectator.query('#test-single-select') as HTMLSelectElement;\n  spectator.selectOption(select, '1');\n  expect(select).toHaveSelectedOptions('1');\n});\n```\n\nIt also allows you to check if your `change` event handler is acting correctly for each item selected. You can disable this if you need to pre set choices without dispatching the change event.\n\nAPI:\n```ts\nspectator.selectOption(selectElement: HTMLSelectElement, options: string | string[] | HTMLOptionElement | HTMLOptionElement[], config: { emitEvents: boolean } = { emitEvents: true });\n```\n\nExample:\n```ts\nit('should dispatch correct number of change events', () =\u003e {\n  const onChangeSpy = spyOn(spectator.component, 'handleChange');\n  const select = spectator.query('#test-onchange-select') as HTMLSelectElement;\n\n  spectator.selectOption(select, ['1', '2'], { emitEvents: true});\n\n  expect(select).toHaveSelectedOptions(['1', '2']);\n  expect(onChangeSpy).toHaveBeenCalledTimes(2);\n});\n\nit('should not dispatch correct number of change events', () =\u003e {\n  const onChangeSpy = spyOn(spectator.component, 'handleChange');\n  const select = spectator.query('#test-onchange-select') as HTMLSelectElement;\n\n  spectator.selectOption(select, ['1', '2'], { emitEvents: false});\n\n  expect(select).toHaveSelectedOptions(['1', '2']);\n  expect(onChangeSpy).not.toHaveBeenCalledTimes(2);\n});\n```\nYou can also pass `HTMLOptionElement`(s) as arguments to `selectOption` and the `toHaveSelectedOptions` matcher. This is particularly useful when you are using `[ngValue]` binding on the `\u003coption\u003e`:\n```ts\nit('should set the correct option on single select when passing the element', () =\u003e {\n  const select = spectator.query('#test-single-select-element') as HTMLSelectElement;\n\n  spectator.selectOption(select, spectator.query(byText('Two')) as HTMLOptionElement);\n\n  expect(select).toHaveSelectedOptions(spectator.query(byText('Two')) as HTMLOptionElement);\n});\n```\n\n#### Mocking Components\nIf you need to mock components, you can use the [ng-mocks](https://github.com/ike18t/ng-mocks) library. Instead of using `CUSTOM_ELEMENTS_SCHEMA`,which might hide some issues and won't help you to set inputs, outputs, etc., `ng-mocks` will auto mock the inputs, outputs, etc. for you.\n\nExample:\n\n```ts\nimport { createHostFactory } from '@ngneat/spectator';\nimport { MockComponent } from 'ng-mocks';\nimport { FooComponent } from './path/to/foo.component';\n\nconst createHost = createHostFactory({\n  component: YourComponentToTest,\n  declarations: [\n    MockComponent(FooComponent)\n  ]\n});\n```\n\n#### Testing Single Component/Directive Angular Modules\n\nComponents (or Directives) that are declared in their own module can be tested by defining the component\nmodule in the imports list of the component factory together with the component. For example:\n\n```ts\nconst createComponent = createComponentFactory({\n  component: ButtonComponent,\n  imports: [ButtonComponentModule],\n});\n```\n\nWhen used like this, however, Spectator internally adds the component `ButtonComponent` to the declarations of the internally created new module. Hence, you will see the following error:\n\n```\nType ButtonComponent is part of the declarations of 2 modules [...]\n```\n\nIt is possible to tell Spectator not to add the component to the declarations of the internal module and, instead, use the explicitly defined module as is. Simply set the `declareComponent` property of the factory options to `false`:\n\n```ts\nconst createComponent = createComponentFactory({\n  component: ButtonComponent,\n  imports: [ButtonComponentModule],\n  declareComponent: false,\n});\n```\n\nWhen using createDirectiveFactory set the `declareDirective` property of the factory options to `false`:\n\n```ts\nconst createDirective = createDirectiveFactory({\n  component: HighlightComponent,\n  imports: [HighlightComponentModule],\n  declareDirective: false,\n});\n```\n\n### Deferrable Views\nThe Spectator provides a convenient API to access the deferrable views (`@defer {}`).\n\nAccess the desired defer block using the `spectator.deferBlock(optionalIndex)` method. The `optionalIndex` parameter is optional and allows you to specify the index of the defer block you want to access.\n\n- **Accessing the first defer block**: Simply call `spectator.deferBlock()`.\n- **Accessing subsequent defer blocks**: Use the corresponding index as an argument. For example, `spectator.deferBlock(1)` accesses the second block (zero-based indexing).\n\nThe `spectator.deferBlock(optionalIndex)` returns four methods for rendering different states of the specified defer block:\n\n- `renderComplete()` - Renders the **complete** state of the defer block.\n- `renderPlaceholder()` - Renders the **placeholder** state of the defer block.\n- `renderLoading()` - Renders the **loading** state of the defer block.\n- `renderError()` - Renders the **error** state of the defer block.\n\n**Example:**\n\n```ts\n    @Component({\n      selector: 'app-cmp',\n      template: `\n        @defer (on viewport) {\n          \u003cdiv\u003eComplete state of the first defer block\u003c/div\u003e \u003c!--Parent Complete State--\u003e\n        } @placeholder {\n          \u003cdiv\u003ePlaceholder\u003c/div\u003e\n        }\n      `,\n    })\n    class DummyComponent {}\n\n    const createComponent = createComponentFactory({\n      component: DummyComponent,\n      deferBlockBehavior: DeferBlockBehavior.Manual,\n    });\n\n    it('should render the complete state', async () =\u003e {\n      // Arrange\n      const spectator = createComponent();\n\n      // Act\n      await spectator.deferBlock().renderComplete();\n\n      // Assert\n      expect(spectator.element.outerHTML).toContain('first defer block');\n    });\n```\n\n\n#### Nested Deferrable Views\n\nTo access states within nested defer blocks, call the `deferBlock` method **chaining** from the returned block state method.\n\n**Example:** Accessing the nested complete state:\n\n```ts\n// Assuming `spectator.deferBlock(0).renderComplete()` renders the complete state of the parent defer block\nconst parentCompleteState = await spectator.deferBlock().renderComplete();\n\n// Access the nested complete state of the parent defer block\nconst nestedCompleteState = await parentCompleteState.renderComplete().deferBlock();\n```\n\n**Complete Example**:\n\n```ts\n    @Component({\n      selector: 'app-cmp',\n      template: `\n        @defer (on viewport) {\n          \u003cdiv\u003eComplete state of the first defer block\u003c/div\u003e \u003c!--Parent Complete State--\u003e\n\n          @defer {\n            \u003cdiv\u003eComplete state of the nested defer block\u003c/div\u003e \u003c!--Nested Complete State--\u003e\n          }\n        } @placeholder {\n          \u003cdiv\u003ePlaceholder\u003c/div\u003e\n        }\n      `,\n    })\n    class DummyComponent {}\n\n    const createComponent = createComponentFactory({\n      component: DummyComponent,\n      deferBlockBehavior: DeferBlockBehavior.Manual,\n    });\n\n    it('should render the first nested complete state', async () =\u003e {\n      // Arrange\n      const spectator = createComponent();\n\n      // Act\n      // Renders the parent complete state\n      const parentCompleteState = await spectator.deferBlock().renderComplete();\n\n      // Renders the nested complete state\n      await parentCompleteState.deferBlock().renderComplete();\n\n      // Assert\n      expect(spectator.element.outerHTML).toContain('nested defer block');\n    });\n```\n\n## Testing with Host\nTesting a component with a host component is a more elegant and powerful technique to test your component.\nIt basically gives you the ability to write your tests in the same way that you write your code. Let's see it in action:\n\n```ts\nimport { createHostFactory, SpectatorHost } from '@ngneat/spectator';\n\ndescribe('ZippyComponent', () =\u003e {\n  let spectator: SpectatorHost\u003cZippyComponent\u003e;\n  const createHost = createHostFactory(ZippyComponent);\n\n  it('should display the title from host property', () =\u003e {\n    spectator = createHost(`\u003czippy [title]=\"title\"\u003e\u003c/zippy\u003e`, {\n      hostProps: {\n        title: 'Spectator is Awesome'\n      }\n    });\n    expect(spectator.query('.zippy__title')).toHaveText('Spectator is Awesome');\n  });\n\n  it('should display the \"Close\" word if open', () =\u003e {\n    spectator = createHost(`\u003czippy title=\"Zippy title\"\u003eZippy content\u003c/zippy\u003e`);\n\n    spectator.click('.zippy__title');\n\n    expect(spectator.query('.arrow')).toHaveText('Close');\n    expect(spectator.query('.arrow')).not.toHaveText('Open');\n  });\n});\n```\nThe host method returns an instance of `SpectatorHost` which extends `Spectator` with the following additional API:\n- `hostFixture` - The host's fixture\n- `hostComponent` - The host's component instance\n- `hostElement` - The host's native element\n- `hostDebugElement` - The host's fixture debug element\n- `setHostInput` -  Changes the value of an `@Input()` of the host component\n- `queryHost` - Read more about querying in Spectator\n- `queryHostAll` - Read more about querying in Spectator\n\nSetting inputs directly on a component using `setInput` or `props` is not possible when testing with a host component.\nInputs should be set through `hostProps` or `setHostInput` instead, and passed through to your component in the template.\n\n### Custom Host Component\nSometimes it's helpful to pass your own host implementation. We can pass a custom host component to the `createHostFactory()` that will replace the default one:\n\n```ts\n@Component({ selector: 'custom-host', template: '' })\nclass CustomHostComponent {\n  title = 'Custom HostComponent';\n}\n\ndescribe('With Custom Host Component', function () {\n  let spectator: SpectatorHost\u003cZippyComponent, CustomHostComponent\u003e;\n  const createHost = createHostFactory({\n    component: ZippyComponent,\n    host: CustomHostComponent\n  });\n\n  it('should display the host component title', () =\u003e {\n    spectator = createHost(`\u003czippy [title]=\"title\"\u003e\u003c/zippy\u003e`);\n    expect(spectator.query('.zippy__title')).toHaveText('Custom HostComponent');\n  });\n});\n```\n\n## Testing with Routing\nFor components which use routing, there is a special factory available that extends the default one, and provides a stubbed `ActivatedRoute` so that you can configure additional routing options.\n\n```ts\ndescribe('ProductDetailsComponent', () =\u003e {\n  let spectator: SpectatorRouting\u003cProductDetailsComponent\u003e;\n  const createComponent = createRoutingFactory({\n    component: ProductDetailsComponent,\n    params: { productId: '3' },\n    data: { title: 'Some title' }\n  });\n\n  beforeEach(() =\u003e spectator = createComponent());\n\n  it('should display route data title', () =\u003e {\n    expect(spectator.query('.title')).toHaveText('Some title');\n  });\n\n  it('should react to route changes', () =\u003e {\n    spectator.setRouteParam('productId', '5');\n\n     // your test here...\n  });\n});\n```\n\n### Triggering a navigation\nThe `SpectatorRouting` API includes convenient methods for updating the current route:\n\n```ts\ninterface SpectatorRouting\u003cC\u003e extends Spectator\u003cC\u003e {\n  /**\n   * Simulates a route navigation by updating the Params, QueryParams and Data observable streams.\n   */\n  triggerNavigation(options?: RouteOptions): void;\n\n  /**\n   * Updates the route params and triggers a route navigation.\n   */\n  setRouteParam(name: string, value: string): void;\n\n  /**\n   * Updates the route query params and triggers a route navigation.\n   */\n  setRouteQueryParam(name: string, value: string): void;\n\n  /**\n   * Updates the route data and triggers a route navigation.\n   */\n  setRouteData(name: string, value: any): void;\n\n  /**\n   * Updates the route fragment and triggers a route navigation.\n   */\n  setRouteFragment(fragment: string | null): void;\n\n  /**\n   * Updates the route url and triggers a route navigation.\n   */\n  setRouteUrl(url: UrlSegment[]): void;\n}\n```\n\n### Integration testing with `RouterTestingModule`\n\nIf you set the `stubsEnabled` option to `false`, you can pass a real routing configuration\nand setup an integration test using the `RouterTestingModule` from Angular.\n\nNote that this requires promises to resolve. One way to deal with this, is by making your test async:\n\n```ts\ndescribe('Routing integration test', () =\u003e {\n  const createComponent = createRoutingFactory({\n    component: MyComponent,\n    declarations: [OtherComponent],\n    stubsEnabled: false,\n    routes: [\n      {\n        path: '',\n        component: MyComponent\n      },\n      {\n        path: 'foo',\n        component: OtherComponent\n      }\n    ]\n  });\n\n  it('should navigate away using router link', async () =\u003e {\n    const spectator = createComponent();\n\n    // wait for promises to resolve...\n    await spectator.fixture.whenStable();\n\n    // test the current route by asserting the location\n    expect(spectator.inject(Location).path()).toBe('/');\n\n    // click on a router link\n    spectator.click('.link-1');\n\n    // don't forget to wait for promises to resolve...\n    await spectator.fixture.whenStable();\n\n    // test the new route by asserting the location\n    expect(spectator.inject(Location).path()).toBe('/foo');\n  });\n});\n```\n\n### Routing Options\n\nThe `createRoutesFactory` function can take the following options, on top of the default Spectator options:\n\n* `params`: initial params to use in `ActivatedRoute` stub\n* `queryParams`: initial query params to use in `ActivatedRoute` stub\n* `data`: initial data to use in `ActivatedRoute` stub\n* `fragment`: initial fragment to use in `ActivatedRoute` stub\n* `url`: initial URL segments to use in `ActivatedRoute` stub\n* `root`: the value for `root` for the `ActivatedRoute` stub\n* `parent`: the value for `parent` for the `ActivatedRoute` stub\n* `children`: the value for `children` for the `ActivatedRoute` stub\n* `firstChild`: the value for `firstChild` for the `ActivatedRoute` stub\n* `stubsEnabled` (default: `true`): enables the `ActivatedRoute` stub, if set to `false` it uses `RouterTestingModule` instead\n* `routes`: if `stubsEnabled` is set to false, you can pass a `Routes` configuration for `RouterTestingModule`\n\n## Testing Directives\n\nThere is a special test factory for testing directives. Let's say we have the following directive:\n\n```ts\n@Directive({ selector: '[highlight]' })\nexport class HighlightDirective {\n\n  @HostBinding('style.background-color') backgroundColor : string;\n\n  @HostListener('mouseover')\n  onHover() {\n    this.backgroundColor = '#000000';\n  }\n\n  @HostListener('mouseout')\n  onLeave() {\n    this.backgroundColor = '#ffffff';\n  }\n}\n```\nLet's see how we can test directives easily with Spectator:\n```ts\ndescribe('HighlightDirective', () =\u003e {\n  let spectator: SpectatorDirective\u003cHighlightDirective\u003e;\n  const createDirective = createDirectiveFactory(HighlightDirective);\n\n  beforeEach(() =\u003e {\n    spectator = createDirective(`\u003cdiv highlight\u003eTesting Highlight Directive\u003c/div\u003e`);\n  });\n\n  it('should change the background color', () =\u003e {\n    spectator.dispatchMouseEvent(spectator.element, 'mouseover');\n\n    expect(spectator.element).toHaveStyle({\n      backgroundColor: 'rgba(0,0,0, 0.1)'\n    });\n\n    spectator.dispatchMouseEvent(spectator.element, 'mouseout');\n    expect(spectator.element).toHaveStyle({\n      backgroundColor: '#fff'\n    });\n  });\n\n  it('should get the instance', () =\u003e {\n    const instance = spectator.directive;\n    expect(instance).toBeDefined();\n  });\n});\n```\n\nSetting inputs directly on a directive using `setInput` or `props` is not possible.\nInputs should be set through `hostProps` or `setHostInput` instead, and passed through to your directive in the template.\n\n## Testing Services\n\nThe following example shows how to test a service with Spectator:\n\n```ts\nimport { createServiceFactory, SpectatorService } from '@ngneat/spectator';\n\nimport { AuthService } from 'auth.service.ts';\n\ndescribe('AuthService', () =\u003e {\n  let spectator: SpectatorService\u003cAuthService\u003e;\n  const createService = createServiceFactory(AuthService);\n\n  beforeEach(() =\u003e spectator = createService());\n\n  it('should not be logged in', () =\u003e {\n    expect(spectator.service.isLoggedIn()).toBeFalsy();\n  });\n});\n```\n\nThe `createService()` function returns `SpectatorService` with the following properties:\n- `service` - Get an instance of the service\n- `inject()` - A proxy for Angular `TestBed.inject()`\n- `flushEffects()` - A proxy for Angular `TestBed.flushEffects()`\n- `runInInjectionContext()` - A proxy for Angular `TestBed.runInInjectionContext()`\n\n### Additional Options\n\nIt's also possible to pass an object with options. For example, when testing a service\nyou often want to mock its dependencies, as we focus on the service being tested.\n\nFor example:\n```ts\n@Injectable()\nexport class AuthService {\n  constructor( private dateService: DateService )  {}\n\n  isLoggedIn() {\n    if( this.dateService.isExpired('timestamp') ) {\n      return false;\n    }\n    return true;\n  }\n}\n```\nIn this case we can mock the `DateService` dependency.\n```ts\nimport { createServiceFactory, SpectatorService } from '@ngneat/spectator';\n\nimport { AuthService } from 'auth.service.ts';\n\ndescribe('AuthService', () =\u003e {\n  let spectator: SpectatorService\u003cAuthService\u003e;\n  const createService = createServiceFactory({\n    service: AuthService,\n    providers: [],\n    entryComponents: [],\n    mocks: [DateService]\n  });\n\n  beforeEach(() =\u003e spectator = createService());\n\n  it('should be logged in', () =\u003e {\n    const dateService = spectator.inject(DateService);\n    dateService.isExpired.and.returnValue(false);\n\n    expect(spectator.service.isLoggedIn()).toBeTruthy();\n  });\n});\n```\n\n## Testing Pipes\n\nThe following example shows how to test a pipe with Spectator:\n\n```ts\nimport { SpectatorPipe, createPipeFactory } from '@ngneat/spectator';\n\nimport { StatsService } from './stats.service';\nimport { SumPipe } from './sum.pipe';\n\ndescribe('SumPipe', () =\u003e {\n  let spectator: SpectatorPipe\u003cSumPipe\u003e;\n  const createPipe = createPipeFactory(SumPipe);\n\n  it('should sum up the given list of numbers (template)', () =\u003e {\n    spectator = createPipe(`{{ [1, 2, 3] | sum }}`);\n    expect(spectator.element).toHaveText('6');\n  });\n\n  it('should sum up the given list of numbers (prop)', () =\u003e {\n    spectator = createPipe(`{{ prop | sum }}`, {\n      hostProps: {\n        prop: [1, 2, 3]\n      }\n    });\n    expect(spectator.element).toHaveText('6');\n  });\n\n  it('should delegate the summation to the service', () =\u003e {\n    const sum = () =\u003e 42;\n    const provider = { provide: StatsService, useValue: { sum } };\n    spectator = createPipe(`{{ prop | sum }}`, {\n      hostProps: {\n        prop: [2, 40]\n      },\n      providers: [provider]\n    });\n    expect(spectator.element).toHaveText('42');\n  });\n});\n```\n\nThe `createPipe()` function returns `SpectatorPipe` with the following properties:\n- `hostComponent` - Instance of the host component\n- `debugElement` - The debug element of the fixture around the host component\n- `element` - The native element of the host component\n- `detectChanges()` - A proxy for Angular `TestBed.fixture.detectChanges()`\n- `inject()` - A proxy for Angular `TestBed.inject()`\n- `flushEffects()` - A proxy for Angular `TestBed.flushEffects()`\n- `runInInjectionContext()` - A proxy for Angular `TestBed.runInInjectionContext()`\n\nSetting inputs directly on a pipe using `setInput` or `props` is not possible.\nInputs should be set through `hostProps` or `setHostInput` instead, and passed through to your pipe in the template.\n\n\n### Using Custom Host Component\n\nThe following example illustrates how to test a pipe using a custom host component:\n\n```ts\nimport { Component, Input } from '@angular/core';\nimport { SpectatorPipe, createPipeFactory } from '@ngneat/spectator';\n\nimport { AveragePipe } from './average.pipe';\nimport { StatsService } from './stats.service';\n\n@Component({\n  template: `\u003cdiv\u003e{{ prop | avg }}\u003c/div\u003e`\n})\nclass CustomHostComponent {\n  @Input() public prop: number[] = [1, 2, 3];\n}\n\ndescribe('AveragePipe', () =\u003e {\n  let spectator: SpectatorPipe\u003cAveragePipe\u003e;\n  const createPipe = createPipeFactory({\n    pipe: AveragePipe,\n    host: CustomHostComponent\n  });\n\n  it('should compute the average of a given list of numbers', () =\u003e {\n    spectator = createPipe();\n    expect(spectator.element).toHaveText('2');\n  });\n\n  it('should result to 0 when list of numbers is empty', () =\u003e {\n    spectator = createPipe({\n      hostProps: {\n        prop: []\n      }\n    });\n    expect(spectator.element).toHaveText('0');\n  });\n\n  it('should delegate the calculation to the service', () =\u003e {\n    const avg = () =\u003e 42;\n    const provider = { provide: StatsService, useValue: { avg } };\n    spectator = createPipe({\n      providers: [provider]\n    });\n    expect(spectator.element).toHaveText('42');\n  });\n});\n```\n\n## Testing DI Functions\n\nEvery Spectator instance supports testing DI Function by passing them to `runInInjectionContext()` function. There is a dedicated test factory that simplifies such testing by eliminating the need to pass some arbitrary Angular class amongst other factory options. Let's say we have a following function that uses the http module to fetch users:\n\n```ts\nimport { HttpClient } from '@angular/common/http';\nimport { inject } from '@angular/core';\n\nfunction getUsers() {\n  return inject(HttpClient).get\u003cUser[]\u003e('users');\n}\n```\n\nLet's see how we can test DI Function easily with Spectator:\n\n```ts\nimport { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';\nimport { createInjectionContextFactory, SpectatorInjectionContext } from '@ngneat/spectator';\n\nfunction getUsers() {\n  return inject(HttpClient).get\u003cUser[]\u003e('users');\n}\n\ndescribe('Users', () =\u003e {\n  let spectator: SpectatorInjectionContext;\n  const createContext = createInjectionContextFactory({ providers: [provideHttpClientTesting()] });\n\n  it('should fetch users', () =\u003e {\n    spectator = createContext();\n    \n    const controller = spectator.inject(HttpTestingController);\n\n    spectator.runInInjectionContext(getUsers).subscribe((users) =\u003e {\n      expect(users.length).toBe(1);\n    });\n\n    controller.expectOne('users').flush([{ id: 1 }]);\n  });\n});\n```\n\nThe `createContext()` function returns `SpectatorInjectionContext` with the following properties:\n- `inject()` - A proxy for Angular `TestBed.inject()`\n- `flushEffects()` - A proxy for Angular `TestBed.flushEffects()`\n- `runInInjectionContext()` - A proxy for Angular `TestBed.runInInjectionContext()`\n\n## Mocking Providers\n\nFor every Spectator factory, we can easily mock any provider.\n\nEvery service that we pass to the `mocks` property will be mocked using the `mockProvider()` function.\nThe `mockProvider()` function converts each method into a Jasmine spy. (i.e `jasmine.createSpy()`).\n\nHere are some of the methods it exposes:\n\n```ts\ndateService.isExpired.and.callThrough();\ndateService.isExpired.and.callFake(() =\u003e fake);\ndateService.isExpired.and.throwError('Error');\ndateService.isExpired.andCallFake(() =\u003e fake);\n```\nHowever, if you use Jest as test framework and you want to utilize its mocking mechanism instead, import the `mockProvider()` from `@ngneat/spectator/jest`.\nThis will automatically use the `jest.fn()` function to create a Jest compatible mock instead.\n\n`mockProvider()` doesn't include properties. In case you need to have properties on your mock you can use 2nd argument:\n```ts\nconst createService = createServiceFactory({\n  service: AuthService,\n  providers: [\n    mockProvider(OtherService, {\n      name: 'Martin',\n      emitter: new Subject(),\n      mockedMethod: () =\u003e 'mocked'\n    })\n  ],\n});\n```\n\n### Mocking OnInit dependencies\n\nIf a component relies on a service being mocked in the [OnInit](https://angular.io/api/core/OnInit) lifecycle method, change-detection needs to be disabled until after the services have been injected.\n\nTo configure this, change the `createComponent` method to have the `detectChanges` option set to false and then manually call `detectChanges` on the spectator after setting up the injected services.\n\n```ts\nconst createComponent = createComponentFactory({\n  component: WeatherDashboardComponent\n});\n\nit('should call the weather api on init', () =\u003e {\n  const spectator = createComponent({\n    detectChanges: false\n  });\n  const weatherService = spectator.inject(WeatherDataApi);\n  weatherService.getWeatherData.andReturn(of(mockWeatherData));\n  spectator.detectChanges();\n  expect(weatherService.getWeatherData).toHaveBeenCalled();\n});\n```\n\n### Mocking constructor dependencies\n\nIf a component relies on a service being mocked in its constructor, you need to create and configure the mock, and to provide the mock when creating the component.\n\n```ts\nconst createComponent = createComponentFactory({\n  component: WeatherDashboardComponent\n});\n\nit('should call the weather api in the constructor', () =\u003e {\n  const weatherService = createSpyObject(WeatherDataApi);\n  weatherService.getWeatherData.andReturn(of(mockWeatherData));\n\n  spectator = createComponent({\n    providers: [\n      { provide: WeatherDataApi, useValue: weatherService }\n    ]\n  });\n\n  expect(weatherService.getWeatherData).toHaveBeenCalled();\n});\n```\n\n## Jest Support\nBy default, Spectator uses Jasmine for creating spies. If you are using Jest as test framework instead, you can let Spectator create Jest-compatible spies.\n\nJust import one of the following functions from `@ngneat/spectator/jest`(instead of @ngneat/spectator), and it will use Jest instead of Jasmine.\n`createComponentFactory()`, `createHostFactory()`, `createServiceFactory()`, `createHttpFactory()`, `mockProvider()`.\n\n```ts\nimport { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';\nimport { AuthService } from './auth.service';\nimport { DateService } from './date.service';\n\ndescribe('AuthService', () =\u003e {\n  let spectator: SpectatorService\u003cAuthService\u003e;\n  const createService = createServiceFactory({\n    service: AuthService,\n    mocks: [DateService]\n  });\n\n  beforeEach(() =\u003e spectator = createService());\n\n  it('should not be logged in', () =\u003e {\n    const dateService = spectator.inject\u003cDateService\u003e(DateService);\n    dateService.isExpired.mockReturnValue(true);\n    expect(spectator.service.isLoggedIn()).toBeFalsy();\n  });\n\n  it('should be logged in', () =\u003e {\n    const dateService = spectator.inject\u003cDateService\u003e(DateService);\n    dateService.isExpired.mockReturnValue(false);\n    expect(spectator.service.isLoggedIn()).toBeTruthy();\n  });\n});\n```\n\nWhen using the component schematic you can specify the `--unitTestRunner` flag to `jest` to have the Jest imports used. In order to Jest imports the default, update `angular.json`:\n```json\n\"schematics\": {\n  \"@ngneat/spectator:spectator-component\": {\n    \"unitTestRunner\": \"jest\"\n  }\n}\n```\n\nPlease note that the previous `--jest` flag is deprecated and will be removed in the future.\n\n## Vitest Support\nLike Jest, Spectator also supports Vitest. \n\nTo use Vitest, update your `vite.config.[m]ts` to inline the Spectator package so it gets transformed with Vite before the tests run.\n\n```ts\nexport default defineConfig(({ mode }) =\u003e ({\n  /* ... */\n  test: {\n    /* ... */\n    // inline @ngneat/spectator\n    server: {\n      deps: {\n        inline: ['@ngneat/spectator']\n      }\n    }\n  },\n}));\n```\n\nYou can then import the functions from `@ngneat/spectator/vitest` instead of `@ngneat/spectator`\nand it will use Vitest instead of Jasmine.\n\n```ts\nimport { createServiceFactory, SpectatorService } from '@ngneat/spectator/vitest';\nimport { AuthService } from './auth.service';\nimport { DateService } from './date.service';\n\ndescribe('AuthService', () =\u003e {\n  let spectator: SpectatorService\u003cAuthService\u003e;\n  const createService = createServiceFactory({\n    service: AuthService,\n    mocks: [DateService]\n  });\n\n  beforeEach(() =\u003e spectator = createService());\n\n  it('should not be logged in', () =\u003e {\n    const dateService = spectator.inject\u003cDateService\u003e(DateService);\n    dateService.isExpired.mockReturnValue(true);\n    expect(spectator.service.isLoggedIn()).toBeFalsy();\n  });\n\n  it('should be logged in', () =\u003e {\n    const dateService = spectator.inject\u003cDateService\u003e(DateService);\n    dateService.isExpired.mockReturnValue(false);\n    expect(spectator.service.isLoggedIn()).toBeTruthy();\n  });\n});\n```\n\nWhen using the component schematic you can specify the `--unitTestRunner` flag to `vitest` to have the Vitest imports used. In order to Vitest imports the default, update `angular.json`:\n```json\n\"schematics\": {\n  \"@ngneat/spectator:spectator-component\": {\n    \"unitTestRunner\": \"vitest\"\n  }\n}\n```\n\n## Testing with HTTP\nSpectator makes testing data services, which use the Angular HTTP module, a lot easier. For example, let's say that you have service with three methods, one performs a GET, one a POST and one performs\nconcurrent requests:\n\n```ts\nexport class TodosDataService {\n  constructor(private httpClient: HttpClient) {}\n\n  getTodos() {\n    return this.httpClient.get('api/todos');\n  }\n\n  postTodo(id: number) {\n    return this.httpClient.post('api/todos', { id });\n  }\n\n  collectTodos() {\n    return merge(\n      this.httpClient.get('/api1/todos'),\n      this.httpClient.get('/api2/todos')\n    );\n  }\n}\n```\n\nThe test for the above service should look like:\n```ts\nimport { createHttpFactory, HttpMethod } from '@ngneat/spectator';\nimport { TodosDataService } from './todos-data.service';\n\ndescribe('HttpClient testing', () =\u003e {\n  let spectator: SpectatorHttp\u003cTodosDataService\u003e;\n  const createHttp = createHttpFactory(TodosDataService);\n\n  beforeEach(() =\u003e spectator = createHttp());\n\n  it('can test HttpClient.get', () =\u003e {\n    spectator.service.getTodos().subscribe();\n    spectator.expectOne('api/todos', HttpMethod.GET);\n  });\n\n  it('can test HttpClient.post', () =\u003e {\n    spectator.service.postTodo(1).subscribe();\n\n    const req = spectator.expectOne('api/todos', HttpMethod.POST);\n    expect(req.request.body['id']).toEqual(1);\n  });\n\n  it('can test current http requests', () =\u003e {\n    spectator.service.getTodos().subscribe();\n    const reqs = spectator.expectConcurrent([\n        { url: '/api1/todos', method: HttpMethod.GET },\n        { URL: '/api2/todos', method: HttpMethod.GET }\n    ]);\n\n    spectator.flushAll(reqs, [{}, {}, {}]);\n  });\n});\n```\nWe need to create an HTTP factory by using the `createHttpFactory()` function, passing the service that you want to test. The `createHttpFactory()` returns a function which can be called to get an instance of SpectatorHttp with the following properties:\n- `controller` - A proxy for Angular `HttpTestingController`\n- `httpClient` - A proxy for Angular `HttpClient`\n- `service` - The service instance\n- `inject()` - A proxy for Angular `TestBed.inject()`\n- `flushEffects()` - A proxy for Angular `TestBed.flushEffects()`\n- `runInInjectionContext()` - A proxy for Angular `TestBed.runInInjectionContext()`\n- `expectOne()` - Expect that a single request was made which matches the given URL and it's method, and return its mock request\n\n\n## Global Injections\nIt's possible to define injections which will be available for each test without the need to re-declare them in each test:\n```ts\n// test.ts\nimport { defineGlobalsInjections } from '@ngneat/spectator';\nimport { TranslocoModule } from '@ngneat/transloco';\n\ndefineGlobalsInjections({\n  imports: [TranslocoModule],\n});\n```\nPlease be aware, that `defineGlobalsInjections()` must be called before the modules are loaded. In the default Angular `test.ts` this means before this line:\n```ts\ncontext.keys().map(context);\n```\n\n## Component Providers\n\nBy default, the original component providers (e.g. the `providers` on the `@Component`) are not touched.\n\nHowever, in most cases, you want to access the component's providers in your test or replace them with mocks.\n\nFor example:\n\n```ts\n@Component({\n  template: '...',\n  providers: [FooService]\n})\nclass FooComponent {\n  constructor(private fooService: FooService} {}\n\n  // ...\n}\n```\n\nUse the `componentProviders` to replace the `FooService` provider:\n\n```ts\nconst createComponent = createComponentFactory({\n  component: FooComponent,\n  componentProviders: [\n    {\n      provide: FooService,\n      useValue: someThingElse\n    }\n  ]\n})\n```\n\nOr mock the service by using `componentMocks`:\n\n```ts\nconst createComponent = createComponentFactory({\n  component: FooComponent,\n  componentMocks: [FooService]\n});\n```\n\nTo access the provider, get it from the component injector using the `fromComponentInjector` parameter:\n\n```ts\nspectator.inject(FooService, true)\n```\n\nIn the same way you can also override the component view providers by using the `componentViewProviders` and `componentViewProvidersMocks`.\n\nThe same rules also apply to directives using the `directiveProviders` and `directiveMocks` parameters.\n\n\n## Custom Matchers\n```ts\nexpect('.zippy__content').not.toExist();\nexpect('.zippy__content').toHaveLength(3);\nexpect('.zippy__content').toHaveId('id');\nexpect(spectator.query('.zippy')).toHaveAttribute('id', 'zippy');\nexpect(spectator.query('.zippy')).toHaveAttribute({id: 'zippy'});\nexpect(spectator.query('.checkbox')).toHaveProperty('checked', true);\nexpect(spectator.query('.img')).toHaveProperty({src: 'assets/myimg.jpg'});\nexpect(spectator.query('.img')).toContainProperty({src: 'myimg.jpg'});\n\n// Note that toHaveClass accepts classes only in strict order. If order is irrelevant, disable strict mode manually.\nexpect('.zippy__content').toHaveClass('class');\nexpect('.zippy__content').toHaveClass('class-a, class-b');\nexpect('.zippy__content').not.toHaveClass('class-b, class-a');\nexpect('.zippy__content').toHaveClass(['class-a', 'class-b']);\nexpect('.zippy__content').not.toHaveClass(['class-b', 'class-a']);\n\nexpect('.zippy__content').toHaveClass('class', { strict: false });\nexpect('.zippy__content').toHaveClass('class-a, class-b', { strict: false });\nexpect('.zippy__content').toHaveClass('class-b, class-a', { strict: false });\nexpect('.zippy__content').toHaveClass(['class-b', 'class-a'], { strict: false });\nexpect('.zippy__content').toHaveClass(['class-b', 'class-a'], { strict: false });\n\n// Note that toHaveText only looks for the existence of a string, not if the string is exactly the same. If you want to verify that the string is completely the same, use toHaveExactText.\n// Note that if you want to verify that the string is completely the same, but trimmed first, use toHaveExactTrimmedText.\n// Note that if you pass multiple values, Spectator checks the text of each array element against the index of the element found.\nexpect('.zippy__content').toHaveText('Content');\nexpect('.zippy__content').toHaveText(['Content A', 'Content B']);\nexpect('.zippy__content').toHaveText((text) =\u003e text.includes('..'));\nexpect('.zippy__content').toContainText('Content');\nexpect('.zippy__content').toContainText(['Content A', 'Content B']);\nexpect('.zippy__content').toHaveExactText('Content');\nexpect('.zippy__content').toHaveExactText(['Content A', 'Content B']);\nexpect('.zippy__content').toHaveExactTrimmedText('Content');\nexpect('.zippy__content').toHaveExactTrimmedText(['Content A', 'Content B']);\nexpect('.zippy__content').toHaveValue('value');\nexpect('.zippy__content').toContainValue('value');\n\n// Note this looks for multiple elements with the class and checks the value of each array element against the index of the element found\nexpect('.zippy__content').toHaveValue(['value a', 'value b']);\nexpect('.zippy__content').toContainValue(['value a', 'value b']);\nexpect(spectator.element).toHaveStyle({backgroundColor: 'rgba(0, 0, 0, 0.1)'});\nexpect('.zippy__content').toHaveData({data: 'role', val: 'admin'});\nexpect('.checkbox').toBeChecked();\nexpect('.checkbox').toBeIndeterminate();\nexpect('.button').toBeDisabled();\nexpect('div').toBeEmpty();\nexpect('div').toBeHidden();\nexpect('element').toBeSelected();\n// Notice that due to restrictions within Jest (not applying actual layout logic in virtual DOM), certain matchers may result in false positives. For example width and height set to 0\nexpect('element').toBeVisible();\nexpect('input').toBeFocused();\nexpect('div').toBeMatchedBy('.js-something');\nexpect(spectator.component.object).toBePartial({ aProperty: 'aValue' });\nexpect('div').toHaveDescendant('.child');\nexpect('div').toHaveDescendantWithText({selector: '.child', text: 'text'});\n```\n## Schematics\nGenerate component, service, and directive with Spectator spec templates with Angular Cli: (when using it as default)\n\n**Component**\n* Default spec: `ng g cs dashrized-name`\n* Spec with a host: `ng g cs dashrized-name --withHost=true`\n* Spec with a custom host: `ng g cs dashrized-name --withCustomHost=true`\n\n**Service:**\n* Default spec: `ng g ss dashrized-name`\n* Spec for testing http data service: `ng g ss dashrized-name --isDataService=true`\n\n**Directive:**\n\n`ng g ds dashrized-name`\n\n## Default Schematics Collection\n\nTo use `spectator` as the default collection in your Angular CLI project,\nadd it to your `angular.json`:\n\n```sh\nng config cli.defaultCollection @ngneat/spectator\n```\n\nThe `spectator` schematics extend the default `@schematics/angular` collection. If you want to set defaults for schematics such as generating components with scss file, you must change the schematics package name from `@schematics/angular` to `@ngneat/spectator` in `angular.json`:\n\n```json\n\"schematics\": {\n  \"@ngneat/spectator:spectator-component\": {\n    \"style\": \"scss\"\n  }\n}\n```\n\n## Working Spectator \u0026 Jest Sample Repo and Karma Comparison\n\nThe [examples in Karma]((https://stackblitz.com/angular/pmqopjovvvb?file=src%2Fapp%2Fapp.component.html)) from Angular docs [testing developer guide](https://angular.io/guide/testing) has been reproduced in Spectator and Jest.\n(For convenience, [this is the local version](https://github.com/muratkeremozcan/books/tree/master/Angular_with_Typescript/angular-unit-testing-with-Karma) of the Karma examples.)\n\nThe Spectator \u0026 Jest version can be accessed [here](https://github.com/muratkeremozcan/angular-playground).\n\n## Core Team\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://www.netbasal.com\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/6745730?v=4\" width=\"100px;\" alt=\"Netanel Basal\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eNetanel Basal\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/dirkluijk\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/2102973?v=4\" width=\"100px;\" alt=\"Dirk Luijk\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDirk Luijk\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://benjaminelliott.co.uk\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/4996462?v=4\" width=\"100px;\" alt=\"Ben Elliott\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBen Elliott\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\n## Contributors\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):~~~~\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/theblushingcrow\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/638818?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eI. Sinai\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=theblushingcrow\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"https://github.com/ngneat/spectator/pulls?q=is%3Apr+reviewed-by%3Atheblushingcrow\" title=\"Reviewed Pull Requests\"\u003e👀\u003c/a\u003e \u003ca href=\"#design-theblushingcrow\" title=\"Design\"\u003e🎨\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/valburyakov\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/18645670?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eValentin Buryakov\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=valburyakov\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#ideas-valburyakov\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/bengry\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/260431?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBen Grynhaus\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/issues?q=author%3Abengry\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e \u003ca href=\"https://github.com/ngneat/spectator/commits?author=bengry\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://www.nuc.cz\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/681176?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMartin Nuc\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=MartinNuc\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://medium.com/@LayZeeDK\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/6364586?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eLars Gyrup Brink Nielsen\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#platform-LayZeeDK\" title=\"Packaging/porting to new platform\"\u003e📦\u003c/a\u003e \u003ca href=\"https://github.com/ngneat/spectator/commits?author=LayZeeDK\" title=\"Tests\"\u003e⚠️\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/thekiba\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1910515?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAndrew Grekov\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=thekiba\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#tool-thekiba\" title=\"Tools\"\u003e🔧\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://twitter.com/jpzwarte\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/3968?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJeroen Zwartepoorte\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=jpzwarte\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/oschlegel\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/11634412?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eOliver Schlegel\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=oschlegel\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/rexebin\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/10893959?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eRex Ye\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#tool-rexebin\" title=\"Tools\"\u003e🔧\u003c/a\u003e \u003ca href=\"https://github.com/ngneat/spectator/commits?author=rexebin\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/tchmura\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/36368505?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003etchmura\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=tchmura\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://www.theneuralnetwork.nl\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/4572798?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eYoeri Nijs\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=YoeriNijs\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/askarby\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/44014?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAnders Skarby\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=askarby\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://medium.com/@gregor.woiwode\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/444278?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eGregor Woiwode\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=GregOnNet\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/asheremetev\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/10629571?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAlexander Sheremetev\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/issues?q=author%3Aasheremetev\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e \u003ca href=\"https://github.com/ngneat/spectator/commits?author=asheremetev\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Hypopheralcus\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/550162?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMike\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=Hypopheralcus\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/mehmet-erim\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/34455572?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMehmet Erim\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=mehmet-erim\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Plysepter\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/8854614?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBrett Eckert\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=Plysepter\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/kanafghan\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/4976816?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eIsmail Faizi\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=kanafghan\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://twitter.com/maxime1992\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/4950209?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMaxime\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=maxime1992\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://e-weap.fr\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/1033191?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJonathan Bonnefoy\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=eweap\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Coly010\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/12140467?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eColum Ferry\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=Coly010\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/cjcoops\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/20629635?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eChris Cooper\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=cjcoops\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/MarcScheib\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/5886709?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMarc Scheib\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=MarcScheib\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/dgsmith2\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/2036391?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003edgsmith2\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=dgsmith2\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/dedwardstech\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/50801476?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ededwardstech\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=dedwardstech\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#ideas-dedwardstech\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/tamasfoldi\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/11571461?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003etamasfoldi\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=tamasfoldi\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#ideas-tamasfoldi\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/IlCallo\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/10036108?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePaolo Caleffi\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=IlCallo\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/tonivj5\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/7110786?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eToni Villena\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=tonivj5\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/itayod\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/6719615?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eItay Oded\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=itayod\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://twitter.com/Wykks\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1236069?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eGuillaume de Jabrun\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=Wykks\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/codeNoobie\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/5392144?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAnand Tiwary\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=codeNoobie\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/AlesDo\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/13190278?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAles Doganoc\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=AlesDo\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://zoltan.nz\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1009783?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eZoltan\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=zoltan-nz\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Coffee-Tea\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/13370430?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eVitalii Baziuk\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=Coffee-Tea\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/clementlemarc-certua\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/50454150?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eclementlemarc-certua\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=clementlemarc-certua\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/th0r\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/302213?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eYuriy Grunin\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=th0r\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://t.me/L2jLiga\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/11780854?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAndrey Chalkin\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=L2jLiga\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://stevenharrisdev.com\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/7720242?s=400\u0026v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSteven Harris\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=Steven-Harris\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/ngneat/spectator/commits?author=Steven-Harris\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://rjgunning.com\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/4697912?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eRichard Sahrakorpi\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=RGunning\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/kremerd\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/13023533?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDominik Kremer\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=kremerd\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://medium.com/@ozanturhan\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/5039606?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMehmet Ozan Turhan\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=ozanturhan\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/thunderer199\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/13765663?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eVlad Lashko\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=thunderer199\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/williamjuan027\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/20136906?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eWilliam Tjondrosuharto\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=williamjuan027\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://twitter.com/Cgatian\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1752170?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eChaz Gatian\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=cgatian\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/shirados90\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/27703381?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePavel Korobov\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=shirados90\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/EnnoLohmann\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/25953659?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eEnno Lohmann\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=EnnoLohmann\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/bogusweb\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/5169399?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePawel Boguslawski\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=bogusweb\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/twittwer\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/8677948?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eTobias Wittwer\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=twittwer\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/ngneat/spectator/commits?author=twittwer\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://matheo.co/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/260185?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMateo Tibaquirá\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ngneat/spectator/commits?author=matheo\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://allcontributors.org/docs/en/emoji-key) specification. Contributions of any kind welcome!\n","funding_links":["https://github.com/sponsors/ngneat"],"categories":["TypeScript","Projects by main language","Testing"],"sub_categories":["angular","Helpers"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngneat%2Fspectator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fngneat%2Fspectator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngneat%2Fspectator/lists"}