├── .all-contributorsrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── 1-bug-report.yaml │ ├── 2-feature-request.yaml │ └── 3-support-request.md └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── angular.json ├── commitlint.config.js ├── docs ├── .gitignore ├── README.md ├── docs │ ├── custom-matchers.md │ ├── events.md │ ├── global-injections.md │ ├── helpers.md │ ├── installation.md │ ├── integration-testing.md │ ├── jest-support.md │ ├── mocking-components.md │ ├── mocking-providers.md │ ├── queries.md │ ├── reference │ │ ├── doc1.md │ │ └── doc2.mdx │ ├── schematics.md │ ├── testing-components.md │ ├── testing-directives.md │ ├── testing-modules.md │ ├── testing-pipes.md │ ├── testing-services.md │ ├── testing-with-host.md │ ├── testing-with-http.md │ ├── testing-with-routing.md │ └── vitest-support.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ └── highlight.js │ ├── css │ │ ├── custom.css │ │ ├── footer.css │ │ ├── home-features.css │ │ ├── home-intro.css │ │ ├── home-layout.css │ │ └── navbar.css │ └── pages │ │ ├── index.js │ │ └── styles.module.css └── static │ ├── .nojekyll │ └── img │ ├── banner.svg │ ├── checkmark.svg │ ├── favicon.ico │ ├── icon │ ├── code-solid.svg │ ├── door-open-solid.svg │ ├── ethernet-regular.svg │ ├── eye-dropper-regular.svg │ ├── head-side-brain-regular.svg │ ├── jest-regular.svg │ ├── main-clean-tests.png │ ├── main-dom-query.png │ ├── main-events.png │ ├── projector-solid.svg │ ├── rabbit-fast-solid.svg │ ├── road-regular.svg │ └── robot-regular.svg │ ├── spectator-feature-bg-sm.jpg │ └── spectator-feature-bg.jpg ├── image.svg ├── package.json ├── projects └── spectator │ ├── .eslintrc.json │ ├── internals │ ├── ng-package.json │ └── src │ │ └── public_api.ts │ ├── jest.config.js │ ├── jest │ ├── ng-package.json │ ├── src │ │ ├── lib │ │ │ ├── dom-selectors.ts │ │ │ ├── matchers-types.ts │ │ │ ├── mock.ts │ │ │ ├── spectator-directive.ts │ │ │ ├── spectator-host.ts │ │ │ ├── spectator-http.ts │ │ │ ├── spectator-injection-context.ts │ │ │ ├── spectator-pipe.ts │ │ │ ├── spectator-routing.ts │ │ │ ├── spectator-service.ts │ │ │ └── spectator.ts │ │ └── public_api.ts │ ├── test │ │ ├── async-input │ │ │ └── async-input.component.spec.ts │ │ ├── async │ │ │ └── async.component.spec.ts │ │ ├── auth.service.spec.ts │ │ ├── auto-focus.directive.spec.ts │ │ ├── button │ │ │ └── button.component.spec.ts │ │ ├── calc.component.spec.ts │ │ ├── click │ │ │ └── click.component.spec.ts │ │ ├── consum-dynamic │ │ │ └── consume-dynamic.component.spec.ts │ │ ├── consumer.service.spec.ts │ │ ├── defer-block.spec.ts │ │ ├── dom-selectors │ │ │ └── dom-selectors.component.spec.ts │ │ ├── fg │ │ │ └── fg.component.spec.ts │ │ ├── focus │ │ │ └── test-focus.component.spec.ts │ │ ├── form-input │ │ │ └── form-input.component.spec.ts │ │ ├── form-select │ │ │ ├── form-select.component.spec.ts │ │ │ └── form-select.component.ts │ │ ├── function-output │ │ │ └── function-output.component.spec.ts │ │ ├── hello │ │ │ └── hello.component.spec.ts │ │ ├── highlight.directive.spec.ts │ │ ├── injection-and-mocking.spec.ts │ │ ├── matchers │ │ │ └── matchers.spec.ts │ │ ├── mock.spec.ts │ │ ├── ngonchanges-input │ │ │ └── ngonchanges-input.component.spec.ts │ │ ├── no-overwritten-providers │ │ │ └── no-overwritten-providers.component.spec.ts │ │ ├── override-component.spec.ts │ │ ├── override-directive.spec.ts │ │ ├── override-module.spec.ts │ │ ├── override-pipe.spec.ts │ │ ├── override-typesafety.component.spec.ts │ │ ├── query-root │ │ │ └── query-root.component.spec.ts │ │ ├── run-in-injection-context.spec.ts │ │ ├── set-input-alias-names.spec.ts │ │ ├── signal-input │ │ │ └── signal-input.component.spec.ts │ │ ├── spy-object │ │ │ └── spy-object.spec.ts │ │ ├── standalone │ │ │ ├── component │ │ │ │ └── standalone.component.spec.ts │ │ │ ├── directive │ │ │ │ └── standalone.directive.spec.ts │ │ │ └── pipe │ │ │ │ └── standalone.pipe.spec.ts │ │ ├── teardown │ │ │ ├── error.ts │ │ │ ├── teardown.component.spec.ts │ │ │ ├── teardown.component.ts │ │ │ └── teardown.service.ts │ │ ├── todos-data.service.spec.ts │ │ ├── unless │ │ │ └── unless.component.spec.ts │ │ ├── view-children │ │ │ └── view-children.component.spec.ts │ │ ├── widget.service.spec.ts │ │ ├── widget │ │ │ └── widget.component.spec.ts │ │ ├── with-routing │ │ │ └── my-page.component.spec.ts │ │ └── zippy │ │ │ └── zippy.component.spec.ts │ └── tsconfig.spec.json │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── schematics │ ├── src │ │ ├── collection.json │ │ └── spectator │ │ │ ├── component-schema.json │ │ │ ├── directive-schema.json │ │ │ ├── files │ │ │ ├── component-custom-host │ │ │ │ └── __name@dasherize__.__type@dasherize__.spec.ts │ │ │ ├── component-host │ │ │ │ └── __name@dasherize__.__type@dasherize__.spec.ts │ │ │ ├── component │ │ │ │ └── __name@dasherize__.__type@dasherize__.spec.ts │ │ │ ├── data-service │ │ │ │ └── __name@dasherize__.service.spec.ts │ │ │ ├── directive │ │ │ │ └── __name@dasherize__.directive.spec.ts │ │ │ ├── pipe │ │ │ │ └── __name@dasherize__.pipe.spec.ts │ │ │ └── service │ │ │ │ └── __name@dasherize__.service.spec.ts │ │ │ ├── index.js │ │ │ ├── index.js.map │ │ │ ├── index.ts │ │ │ ├── pipe-schema.json │ │ │ ├── schema.js │ │ │ ├── schema.js.map │ │ │ ├── schema.ts │ │ │ └── service-schema.json │ └── tsconfig.json │ ├── setup-jest.ts │ ├── setup-vitest.ts │ ├── src │ ├── lib │ │ ├── base │ │ │ ├── base-spectator.ts │ │ │ ├── dom-spectator.ts │ │ │ ├── initial-module.ts │ │ │ └── options.ts │ │ ├── core.ts │ │ ├── dispatch-events.ts │ │ ├── dom-selectors.ts │ │ ├── event-creators.ts │ │ ├── globals-injections.ts │ │ ├── internals │ │ │ ├── element-focus.ts │ │ │ ├── key-parser.ts │ │ │ ├── merge.ts │ │ │ ├── node-by-directive.ts │ │ │ ├── query.ts │ │ │ └── rgb-to-hex.ts │ │ ├── matchers-types.ts │ │ ├── matchers.ts │ │ ├── mock.ts │ │ ├── select-option.ts │ │ ├── spectator-directive │ │ │ ├── create-factory.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ └── spectator-directive.ts │ │ ├── spectator-host │ │ │ ├── create-factory.ts │ │ │ ├── host-component.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ └── spectator-host.ts │ │ ├── spectator-http │ │ │ ├── create-factory.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ └── spectator-http.ts │ │ ├── spectator-injection-context │ │ │ ├── create-factory.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ └── spectator-injection-context.ts │ │ ├── spectator-pipe │ │ │ ├── create-factory.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ └── spectator-pipe.ts │ │ ├── spectator-routing │ │ │ ├── activated-route-stub.ts │ │ │ ├── create-factory.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ ├── route-options.ts │ │ │ ├── router-stub.ts │ │ │ └── spectator-routing.ts │ │ ├── spectator-service │ │ │ ├── create-factory.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ └── spectator-service.ts │ │ ├── spectator │ │ │ ├── create-factory.ts │ │ │ ├── initial-module.ts │ │ │ ├── options.ts │ │ │ └── spectator.ts │ │ ├── token.ts │ │ ├── type-in-element.ts │ │ ├── types.ts │ │ └── utils.ts │ └── public_api.ts │ ├── test │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.module.ts │ ├── async-input │ │ ├── async-input.component.spec.ts │ │ └── async-input.component.ts │ ├── async │ │ ├── async.component.spec.ts │ │ └── async.component.ts │ ├── auth.service.spec.ts │ ├── auth.service.ts │ ├── auto-focus │ │ ├── auto-focus.directive.spec.ts │ │ ├── auto-focus.directive.ts │ │ ├── auto-focus.module.spec.ts │ │ └── auto-focus.module.ts │ ├── button │ │ ├── button.component.spec.ts │ │ └── button.component.ts │ ├── calc-textarea │ │ ├── calc-textarea.component.spec.ts │ │ └── calc-textarea.component.ts │ ├── calc │ │ ├── calc.component.spec.ts │ │ └── calc.component.ts │ ├── child-custom-event │ │ ├── child-custom-event-parent.component.spec.ts │ │ ├── child-custom-event-parent.component.ts │ │ ├── child-custom-event.component.ts │ │ └── child-custom-event.module.ts │ ├── child-service.service.spec.ts │ ├── child-service.service.ts │ ├── child │ │ └── child.component.ts │ ├── click │ │ ├── click.component.spec.ts │ │ └── click.component.ts │ ├── component-providers.spec.ts │ ├── consum-dynamic │ │ ├── consume-dynamic.component.spec.ts │ │ └── consume-dynamic.component.ts │ ├── consumer.service.spec.ts │ ├── consumer.service.ts │ ├── date.service.spec.ts │ ├── date.service.ts │ ├── defer-block.spec.ts │ ├── directive-providers.directive.spec.ts │ ├── directive-providers.directive.ts │ ├── dom-selectors │ │ ├── dom-selectors.component.spec.ts │ │ └── dom-selectors.component.ts │ ├── download │ │ └── download.component.ts │ ├── dropzone │ │ ├── dropzone.component.spec.ts │ │ └── dropzone.component.ts │ ├── dynamic │ │ └── dynamic.component.ts │ ├── error-unknown │ │ ├── error-unknown-element.component.spec.ts │ │ ├── error-unknown-element.component.ts │ │ ├── error-unknown-property.component.spec.ts │ │ └── error-unknown-property.component.ts │ ├── events │ │ ├── events.component.html │ │ ├── events.component.scss │ │ ├── events.component.spec.ts │ │ └── events.component.ts │ ├── fg │ │ ├── fg.component.spec.ts │ │ └── fg.component.ts │ ├── focus │ │ ├── test-focus.component.spec.ts │ │ └── test-focus.component.ts │ ├── form-input │ │ ├── form-input.component.html │ │ ├── form-input.component.scss │ │ ├── form-input.component.spec.ts │ │ └── form-input.component.ts │ ├── form-select │ │ ├── form-select.component.spec.ts │ │ └── form-select.component.ts │ ├── function-output │ │ ├── function-output.component.spec.ts │ │ └── function-output.component.ts │ ├── hello │ │ ├── hello.component.css │ │ ├── hello.component.html │ │ ├── hello.component.spec.ts │ │ └── hello.component.ts │ ├── highlight.directive.spec.ts │ ├── highlight.directive.ts │ ├── injection-and-mocking.spec.ts │ ├── integration │ │ ├── integration-child.component.ts │ │ ├── integration-parent.component.spec.ts │ │ ├── integration-parent.component.ts │ │ └── integration.module.ts │ ├── matchers │ │ ├── matcher-enhancements.component.spec.ts │ │ └── matcher-enhancements.component.ts │ ├── mocks.spec.ts │ ├── ng-on-destroy.service.spec.ts │ ├── ng-on-destroy.service.ts │ ├── ngonchanges-input │ │ ├── ngonchanges-input.component.spec.ts │ │ └── ngonchanges-input.component.ts │ ├── no-overwritten-providers │ │ ├── dummy.service.ts │ │ ├── no-overwritten-providers.component.spec.ts │ │ └── no-overwritten-providers.component.ts │ ├── overlay-custom-event │ │ ├── overlay-container.component.spec.ts │ │ ├── overlay-container.component.ts │ │ ├── overlay-container.module.ts │ │ └── overlay-content.component.ts │ ├── override-component.spec.ts │ ├── override-directive.spec.ts │ ├── override-module.spec.ts │ ├── override-pipe.spec.ts │ ├── override-typesafety.component.spec.ts │ ├── pipe │ │ ├── alternating-sum.pipe.spec.ts │ │ ├── alternating-sum.pipe.ts │ │ ├── average.pipe.spec.ts │ │ ├── average.pipe.ts │ │ ├── stats.service.ts │ │ ├── sum.pipe.spec.ts │ │ └── sum.pipe.ts │ ├── provider.service.ts │ ├── query-root │ │ ├── query-root.component.spec.ts │ │ └── query-root.component.ts │ ├── query.service.ts │ ├── run-in-injection-context.spec.ts │ ├── set-input-alias-names.spec.ts │ ├── set-input │ │ ├── set-input.component.spec.ts │ │ └── set-input.component.ts │ ├── signal-input │ │ ├── signal-input.component.spec.ts │ │ └── signal-input.component.ts │ ├── simple-changes │ │ ├── simple-changes.component.spec.ts │ │ └── simple-changes.component.ts │ ├── spectator-routing │ │ └── activated-route-stub.spec.ts │ ├── spy-object │ │ ├── person.ts │ │ └── spy-object.spec.ts │ ├── standalone │ │ ├── component │ │ │ ├── standalone-with-imports.component.spec.ts │ │ │ ├── standalone-with-imports.component.ts │ │ │ ├── standalone.component.spec.ts │ │ │ └── standalone.component.ts │ │ ├── directive │ │ │ ├── standalone.directive.spec.ts │ │ │ └── standalone.directive.ts │ │ └── pipe │ │ │ ├── standalone.pipe.spec.ts │ │ │ └── standalone.pipe.ts │ ├── teardown │ │ ├── error.ts │ │ ├── teardown.component.spec.ts │ │ ├── teardown.component.ts │ │ └── teardown.service.ts │ ├── test.ts │ ├── todos-data.service.spec.ts │ ├── todos-data.service.ts │ ├── translate.pipe.ts │ ├── translate.service.ts │ ├── unless │ │ ├── unless.component.spec.ts │ │ └── unless.component.ts │ ├── view-children │ │ ├── view-children.component.spec.ts │ │ └── view-children.component.ts │ ├── widget-data.service.ts │ ├── widget.service.spec.ts │ ├── widget.service.ts │ ├── widget │ │ ├── widget.component.spec.ts │ │ └── widget.component.ts │ ├── with-routing │ │ ├── my-page.component.spec.ts │ │ └── my-page.component.ts │ └── zippy │ │ ├── zippy.component.spec.ts │ │ └── zippy.component.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ ├── typings │ └── jest │ │ └── index.d.ts │ ├── vite.config.mts │ └── vitest │ ├── ng-package.json │ ├── src │ ├── lib │ │ ├── dom-selectors.ts │ │ ├── matchers-types.ts │ │ ├── mock.ts │ │ ├── spectator-directive.ts │ │ ├── spectator-host.ts │ │ ├── spectator-http.ts │ │ ├── spectator-injection-context.ts │ │ ├── spectator-pipe.ts │ │ ├── spectator-routing.ts │ │ ├── spectator-service.ts │ │ └── spectator.ts │ └── public_api.ts │ ├── test │ ├── async-input │ │ └── async-input.component.spec.ts │ ├── async │ │ └── async.component.spec.ts │ ├── auth.service.spec.ts │ ├── auto-focus.directive.spec.ts │ ├── button │ │ └── button.component.spec.ts │ ├── calc.component.spec.ts │ ├── click │ │ └── click.component.spec.ts │ ├── consum-dynamic │ │ └── consume-dynamic.component.spec.ts │ ├── consumer.service.spec.ts │ ├── defer-block.spec.ts │ ├── dom-selectors │ │ └── dom-selectors.component.spec.ts │ ├── fg │ │ └── fg.component.spec.ts │ ├── focus │ │ └── test-focus.component.spec.ts │ ├── form-input │ │ └── form-input.component.spec.ts │ ├── form-select │ │ ├── form-select.component.spec.ts │ │ └── form-select.component.ts │ ├── function-output │ │ └── function-output.component.spec.ts │ ├── hello │ │ └── hello.component.spec.ts │ ├── highlight.directive.spec.ts │ ├── injection-and-mocking.spec.ts │ ├── matchers │ │ └── matchers.spec.ts │ ├── mock.spec.ts │ ├── ngonchanges-input │ │ └── ngonchanges-input.component.spec.ts │ ├── no-overwritten-providers │ │ └── no-overwritten-providers.component.spec.ts │ ├── override-component.spec.ts │ ├── override-directive.spec.ts │ ├── override-module.spec.ts │ ├── override-pipe.spec.ts │ ├── override-typesafety.component.spec.ts │ ├── query-root │ │ └── query-root.component.spec.ts │ ├── run-in-injection-context.spec.ts │ ├── set-input-alias-names.spec.ts │ ├── signal-input │ │ └── signal-input.component.spec.ts │ ├── spy-object │ │ └── spy-object.spec.ts │ ├── standalone │ │ ├── component │ │ │ └── standalone.component.spec.ts │ │ ├── directive │ │ │ └── standalone.directive.spec.ts │ │ └── pipe │ │ │ └── standalone.pipe.spec.ts │ ├── teardown │ │ ├── error.ts │ │ ├── teardown.component.spec.ts │ │ ├── teardown.component.ts │ │ └── teardown.service.ts │ ├── todos-data.service.spec.ts │ ├── unless │ │ └── unless.component.spec.ts │ ├── view-children │ │ └── view-children.component.spec.ts │ ├── widget.service.spec.ts │ ├── widget │ │ └── widget.component.spec.ts │ ├── with-routing │ │ └── my-page.component.spec.ts │ └── zippy │ │ └── zippy.component.spec.ts │ └── tsconfig.spec.json ├── tsconfig.json ├── vscode-snippets ├── CHANGELOG.md ├── README.md ├── logo.png ├── package.json ├── snippets │ └── snippets.json └── vsc-extension-quickstart.md └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | projects/spectator/test/**/*.ts 2 | projects/spectator/jest/test/**/*.ts 3 | projects/spectator/vitest/test/**/*.ts 4 | projects/spectator/schematics/**/*.* 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json", 14 | "e2e/tsconfig.json" 15 | ], 16 | "createDefaultProgram": true 17 | }, 18 | "extends": [ 19 | "plugin:@angular-eslint/recommended", 20 | "plugin:@angular-eslint/template/process-inline-templates" 21 | ], 22 | "rules": { 23 | "@angular-eslint/component-selector": [ 24 | "error", 25 | { 26 | "prefix": "lib", 27 | "style": "kebab-case", 28 | "type": "element" 29 | } 30 | ], 31 | "@angular-eslint/directive-selector": [ 32 | "error", 33 | { 34 | "prefix": "lib", 35 | "style": "camelCase", 36 | "type": "attribute" 37 | } 38 | ] 39 | } 40 | }, 41 | { 42 | "files": [ 43 | "*.html" 44 | ], 45 | "extends": [ 46 | "plugin:@angular-eslint/template/recommended" 47 | ], 48 | "rules": {} 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug in the Spectator Library 3 | 4 | body: 5 | - type: dropdown 6 | id: is-regression 7 | attributes: 8 | label: Is this a regression? 9 | options: 10 | - 'Yes' 11 | - 'No' 12 | validations: 13 | required: true 14 | 15 | - type: textarea 16 | id: description 17 | attributes: 18 | label: Description 19 | validations: 20 | required: true 21 | 22 | - type: input 23 | id: reproduction 24 | attributes: 25 | label: Please provide a link to a minimal reproduction of the bug 26 | 27 | - type: textarea 28 | id: exception-or-error 29 | attributes: 30 | label: Please provide the exception or error you saw 31 | render: true 32 | 33 | - type: textarea 34 | id: environment 35 | attributes: 36 | label: Please provide the environment you discovered this bug in 37 | render: true 38 | 39 | - type: textarea 40 | id: other 41 | attributes: 42 | label: Anything else? 43 | 44 | - type: dropdown 45 | id: contribute 46 | attributes: 47 | label: Do you want to create a pull request? 48 | options: 49 | - 'Yes' 50 | - 'No' 51 | validations: 52 | required: true 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: 'Feature Request' 2 | description: Suggest a feature for Spectator Library 3 | 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Description 9 | validations: 10 | required: true 11 | 12 | - type: textarea 13 | id: proposed-solution 14 | attributes: 15 | label: Proposed solution 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | id: alternatives-considered 21 | attributes: 22 | label: Alternatives considered 23 | validations: 24 | required: true 25 | 26 | - type: dropdown 27 | id: contribute 28 | attributes: 29 | label: Do you want to create a pull request? 30 | options: 31 | - 'Yes' 32 | - 'No' 33 | validations: 34 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-support-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Support Request' 3 | about: Questions and requests for support 4 | --- 5 | 6 | Please do not file questions or support requests on the GitHub issues tracker. 7 | 8 | You can get your questions answered using other communication channels. Please see: 9 | 10 | https://github.com/ngneat/spectator/discussions 11 | 12 | Thank you! 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.angular/cache 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | 49 | # vitest 50 | vite.config.mts.timestamp-*.mjs 51 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | projects/spectator/schematics/src/spectator/files/**/*.* 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "printWidth": 140, 6 | "parser": "typescript" 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "activityBar.activeBackground": "#ab307e", 4 | "activityBar.activeBorder": "#25320e", 5 | "activityBar.background": "#ab307e", 6 | "activityBar.foreground": "#e7e7e7", 7 | "activityBar.inactiveForeground": "#e7e7e799", 8 | "activityBarBadge.background": "#25320e", 9 | "activityBarBadge.foreground": "#e7e7e7", 10 | "statusBar.background": "#832561", 11 | "statusBar.foreground": "#e7e7e7", 12 | "statusBarItem.hoverBackground": "#ab307e", 13 | "titleBar.activeBackground": "#832561", 14 | "titleBar.activeForeground": "#e7e7e7", 15 | "titleBar.inactiveBackground": "#83256199", 16 | "titleBar.inactiveForeground": "#e7e7e799", 17 | "sash.hoverBorder": "#ab307e", 18 | "statusBarItem.remoteBackground": "#832561", 19 | "statusBarItem.remoteForeground": "#e7e7e7" 20 | }, 21 | "peacock.color": "#832561" 22 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Netanel Basal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | Please check if your PR fulfills the following requirements: 3 | 4 | - [ ] The commit message follows our guidelines: https://github.com/ngneat/spectator/blob/master/CONTRIBUTING.md#commit 5 | - [ ] Tests for the changes have been added (for bug fixes / features) 6 | - [ ] Docs have been added / updated (for bug fixes / features) 7 | 8 | 9 | ## PR Type 10 | What kind of change does this PR introduce? 11 | 12 | 13 | ``` 14 | [ ] Bugfix 15 | [ ] Feature 16 | [ ] Code style update (formatting, local variables) 17 | [ ] Refactoring (no functional changes, no api changes) 18 | [ ] Build related changes 19 | [ ] CI related changes 20 | [ ] Documentation content changes 21 | [ ] Other... Please describe: 22 | ``` 23 | 24 | ## What is the current behavior? 25 | 26 | 27 | Issue Number: N/A 28 | 29 | 30 | ## What is the new behavior? 31 | 32 | 33 | ## Does this PR introduce a breaking change? 34 | ``` 35 | [ ] Yes 36 | [ ] No 37 | ``` 38 | 39 | 40 | 41 | 42 | ## Other information 43 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] } 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | ``` 30 | $ GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /docs/docs/global-injections.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: global-injections 3 | title: Global Injections 4 | --- 5 | 6 | It's possible to define injections which will be available for each test without the need to re-declare them in each test: 7 | ```ts 8 | // test.ts 9 | import { defineGlobalsInjections } from '@ngneat/spectator'; 10 | import { TranslocoModule } from '@ngneat/transloco'; 11 | 12 | defineGlobalsInjections({ 13 | imports: [TranslocoModule], 14 | }); 15 | ``` 16 | 17 | Please be aware, that `defineGlobalsInjections()` must be called before the modules are loaded. In the default Angular `test.ts` this means before this line: 18 | 19 | ```ts 20 | context.keys().map(context); 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/docs/helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: helpers 3 | title: Helpers 4 | --- 5 | 6 | ## Keyboard helpers 7 | ```ts 8 | spectator.keyboard.pressEnter(); 9 | spectator.keyboard.pressEscape(); 10 | spectator.keyboard.pressTab(); 11 | spectator.keyboard.pressBackspace(); 12 | spectator.keyboard.pressKey('a'); 13 | spectator.keyboard.pressKey('ctrl.a'); 14 | spectator.keyboard.pressKey('ctrl.shift.a'); 15 | ``` 16 | 17 | ## Mouse helpers 18 | ```ts 19 | spectator.mouse.contextmenu('.selector'); 20 | spectator.mouse.dblclick('.selector'); 21 | ``` 22 | 23 | Note that each one of the above methods will also run `detectChanges()`. 24 | -------------------------------------------------------------------------------- /docs/docs/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: installation 3 | title: Installation 4 | --- 5 | 6 | > A Powerful Tool to Simplify Your Angular Tests 7 | 8 | Spectator helps you get rid of all the boilerplate grunt work, leaving you with readable, sleek and streamlined unit tests. 9 | 10 | ## Features 11 | - ✅ Support for testing Angular components, directives and services 12 | - ✅ Easy DOM querying 13 | - ✅ Clean API for triggering keyboard/mouse/touch events 14 | - ✅ Testing `ng-content` 15 | - ✅ Custom Jasmine/Jest Matchers (toHaveClass, toBeDisabled..) 16 | - ✅ Routing testing support 17 | - ✅ HTTP testing support 18 | - ✅ Built-in support for entry components 19 | - ✅ Built-in support for component providers 20 | - ✅ Auto-mocking providers 21 | - ✅ Strongly typed 22 | - ✅ Jest Support 23 | 24 | ### NPM 25 | 26 | `npm install @ngneat/spectator --save-dev` 27 | 28 | ### Yarn 29 | 30 | `yarn add @ngneat/spectator --dev` 31 | -------------------------------------------------------------------------------- /docs/docs/integration-testing.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/docs/integration-testing.md -------------------------------------------------------------------------------- /docs/docs/mocking-components.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: mocking-components 3 | title: Mocking Components 4 | --- 5 | 6 | If you need to mock dependencies such as modules, components and other declarations, you can use the [ng-mocks](https://ng-mocks.sudo.eu) library and its [`MockBuilder`](https://ng-mocks.sudo.eu/extra/with-3rd-party#ngneatspectator-and-mockbuilder). 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. 7 | 8 | Example: 9 | 10 | ```ts 11 | import { createHostFactory } from '@ngneat/spectator'; 12 | import { MockBuilder } from 'ng-mocks'; 13 | import { FooComponent } from './path/to/foo.component'; 14 | 15 | // All imports, declarations and exports of ItsModule will be mocked. 16 | // The same applies to standalone components, simply omit the module. 17 | const dependencies = MockBuilder(YourComponentToTest, ItsModule).build(); 18 | 19 | const createHost = createHostFactory({ 20 | component: YourComponentToTest, 21 | ...dependencies, 22 | }); 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/docs/mocking-providers.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: mocking-providers 3 | title: Mocking Providers 4 | --- 5 | 6 | For every Spectator factory, we can easily mock any provider. 7 | 8 | Every service that we pass to the `mocks` property will be mocked using the `mockProvider()` function. 9 | The `mockProvider()` function converts each method into a Jasmine spy. (i.e `jasmine.createSpy()`). 10 | 11 | Here are some of the methods it exposes: 12 | 13 | ```ts 14 | dateService.isExpired.and.callThrough(); 15 | dateService.isExpired.and.callFake(() => fake); 16 | dateService.isExpired.and.throwError('Error'); 17 | dateService.isExpired.andCallFake(() => fake); 18 | ``` 19 | However, if you use Jest as test framework and you want to utilize its mocking mechanism instead, import the `mockProvider()` from `@ngneat/spectator/jest`. 20 | This will automatically use the `jest.fn()` function to create a Jest compatible mock instead. 21 | 22 | `mockProvider()` doesn't include properties. In case you need to have properties on your mock you can use 2nd argument: 23 | ```ts 24 | const createService = createServiceFactory({ 25 | service: AuthService, 26 | providers: [ 27 | mockProvider(OtherService, { 28 | name: 'Martin', 29 | emitter: new Subject(), 30 | mockedMethod: () => 'mocked' 31 | }) 32 | ], 33 | }); 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/docs/reference/doc2.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: doc2 3 | title: Document Number 2 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | 17 | 18 | 19 | ```javascript title="src/components/HelloCodeTitle.js" 20 | function HelloCodeTitle(props) { 21 | } 22 | ``` 23 | 24 | 25 | 26 | 27 | ```py 28 | def hello_world(): 29 | print 'Hello, world!' 30 | ``` 31 | 32 | 33 | 34 | 35 | ```java 36 | class HelloWorld { 37 | public static void main(String args[]) { 38 | System.out.println("Hello, World"); 39 | } 40 | } 41 | ``` 42 | 43 | 44 | 45 | 46 | export const Highlight = ({children, color}) => ( 47 | 54 | {children} 55 | 56 | ); 57 | 58 | Docusaurus green and Facebook blue are my favorite colors. 59 | 60 | I can write **Markdown** alongside my _JSX_! 61 | -------------------------------------------------------------------------------- /docs/docs/schematics.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: schematics 3 | title: Schematics 4 | --- 5 | 6 | Generate component, service, and directive with Spectator spec templates with Angular Cli: (when using it as default) 7 | 8 | **Component** 9 | * Default spec: `ng g cs dashrized-name` 10 | * Spec with a host: `ng g cs dashrized-name --withHost=true` 11 | * Spec with a custom host: `ng g cs dashrized-name --withCustomHost=true` 12 | 13 | **Service:** 14 | * Default spec: `ng g ss dashrized-name` 15 | * Spec for testing http data service: `ng g ss dashrized-name --isDataService=true` 16 | 17 | **Directive:** 18 | 19 | `ng g ds dashrized-name` 20 | 21 | ## Default Schematics Collection 22 | 23 | To use `spectator` as the default collection in your Angular CLI project, 24 | add it to your `angular.json`: 25 | 26 | ```sh 27 | ng config cli.defaultCollection @ngneat/spectator 28 | ``` 29 | 30 | The `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`: 31 | 32 | ```json 33 | "schematics": { 34 | "@ngneat/spectator:spectator-component": { 35 | "style": "scss" 36 | } 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spectator-docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "GIT_USER=NetanelBasal USE_SSH=true docusaurus deploy" 10 | }, 11 | "dependencies": { 12 | "@docusaurus/core": "^2.0.0-alpha.54", 13 | "@docusaurus/preset-classic": "^2.0.0-alpha.54", 14 | "classnames": "^2.2.6", 15 | "react": "^16.8.4", 16 | "react-dom": "^16.8.4" 17 | }, 18 | "browserslist": { 19 | "production": [ 20 | ">0.2%", 21 | "not dead", 22 | "not op_mini all" 23 | ], 24 | "development": [ 25 | "last 1 chrome version", 26 | "last 1 firefox version", 27 | "last 1 safari version" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | docs: [ 3 | { 4 | type: 'doc', 5 | id: 'installation' 6 | }, 7 | { 8 | type: 'category', 9 | label: 'Use Cases', 10 | items: [ 11 | 'testing-components', 12 | 'testing-directives', 13 | 'testing-pipes', 14 | 'testing-services', 15 | 'testing-with-host', 16 | 'testing-with-http', 17 | 'testing-with-routing', 18 | 'testing-modules' 19 | ] 20 | }, 21 | { 22 | type: 'category', 23 | label: 'Tools', 24 | items: ['schematics', 'custom-matchers', 'global-injections', 'mocking-components'] 25 | }, 26 | { 27 | type: 'category', 28 | label: 'API Reference', 29 | items: ['events', 'helpers', 'queries'] 30 | }, 31 | { 32 | type: 'doc', 33 | id: 'jest-support' 34 | } 35 | ] 36 | }; 37 | -------------------------------------------------------------------------------- /docs/src/components/highlight.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const Highlight = ({ children, bgColor, color }) => ( 4 | 12 | {' '} 13 | {children}{' '} 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /docs/src/css/footer.css: -------------------------------------------------------------------------------- 1 | footer.footer.footer--dark { 2 | font-family: 'Roboto', sans-serif; 3 | background: #002D3D; 4 | } 5 | 6 | footer.footer.footer--dark .container { 7 | display:flex; 8 | flex-direction: column; 9 | align-items: center; 10 | justify-content: center; 11 | margin-top: 54px; 12 | } 13 | @media screen and (max-width: 800px) { 14 | footer.footer.footer--dark .container { 15 | margin-top: 34px; 16 | } 17 | } 18 | 19 | .row.footer__links { 20 | display:flex; 21 | justify-content: space-between; 22 | width: 524px; 23 | max-width: 100%; 24 | margin-bottom: 112px; 25 | } 26 | @media screen and (max-width: 800px) { 27 | .row.footer__links { 28 | margin-bottom: 6px; 29 | } 30 | } 31 | 32 | h4.footer__title { 33 | margin-bottom: 24px; 34 | font-family: 'Montserrat', sans-serif; 35 | font-size: 15px; 36 | font-weight: 800; 37 | text-transform: uppercase; 38 | opacity: 0.9; 39 | } 40 | 41 | a.footer__link-item { 42 | font-size: 16px; 43 | font-weight: 500; 44 | opacity: 0.6; 45 | } 46 | 47 | footer.footer.footer--dark .text--center > div { 48 | font-size: 12px; 49 | opacity:0.5; 50 | } -------------------------------------------------------------------------------- /docs/src/css/home-layout.css: -------------------------------------------------------------------------------- 1 | .home-container { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | } 6 | 7 | .home-row { 8 | width: 1000px; 9 | max-width: 1000px; 10 | } -------------------------------------------------------------------------------- /docs/src/css/navbar.css: -------------------------------------------------------------------------------- 1 | .navbar .navbar__link, 2 | .navbar .navbar__brand { 3 | color: #49BCED; 4 | font-family: 'Montserrat', sans-serif; 5 | font-size: 12px; 6 | font-weight: 600; 7 | text-transform: uppercase; 8 | } 9 | .navbar .navbar__item:hover, 10 | .navbar .navbar__brand:hover { 11 | color: #79d7ff; 12 | } 13 | 14 | .navbar__link.navbar__item.navbar__link--active { 15 | color: #fff; 16 | font-weight: 900; 17 | } -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/checkmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/icon/code-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/door-open-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/ethernet-regular.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/eye-dropper-regular.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/head-side-brain-regular.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/main-clean-tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/static/img/icon/main-clean-tests.png -------------------------------------------------------------------------------- /docs/static/img/icon/main-dom-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/static/img/icon/main-dom-query.png -------------------------------------------------------------------------------- /docs/static/img/icon/main-events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/static/img/icon/main-events.png -------------------------------------------------------------------------------- /docs/static/img/icon/projector-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/rabbit-fast-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/road-regular.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/icon/robot-regular.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/spectator-feature-bg-sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/static/img/spectator-feature-bg-sm.jpg -------------------------------------------------------------------------------- /docs/static/img/spectator-feature-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/docs/static/img/spectator-feature-bg.jpg -------------------------------------------------------------------------------- /projects/spectator/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "projects/spectator/tsconfig.lib.json", 14 | "projects/spectator/tsconfig.spec.json" 15 | ], 16 | "createDefaultProgram": true 17 | }, 18 | "rules": { 19 | "@angular-eslint/directive-selector": [ 20 | "error", 21 | { 22 | "type": "attribute", 23 | "prefix": "lib", 24 | "style": "camelCase" 25 | } 26 | ], 27 | "@angular-eslint/component-selector": [ 28 | "error", 29 | { 30 | "type": "element", 31 | "prefix": "lib", 32 | "style": "kebab-case" 33 | } 34 | ] 35 | } 36 | }, 37 | { 38 | "files": [ 39 | "*.html" 40 | ], 41 | "rules": {} 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /projects/spectator/internals/ng-package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /projects/spectator/jest.config.js: -------------------------------------------------------------------------------- 1 | const { defaultTransformerOptions } = require('jest-preset-angular/presets'); 2 | 3 | module.exports = { 4 | transform: { 5 | '^.+\\.(ts|js|mjs|html|svg)$': [ 6 | 'jest-preset-angular', 7 | { 8 | ...defaultTransformerOptions, 9 | tsconfig: 'projects/spectator/jest/tsconfig.spec.json', 10 | isolatedModules: true, 11 | }, 12 | ], 13 | }, 14 | roots: ['projects/spectator'], 15 | testMatch: ['**/jest/**/*.spec.ts'], 16 | setupFilesAfterEnv: ['/projects/spectator/setup-jest.ts'], 17 | moduleNameMapper: { 18 | '@ngneat/spectator/internals': '/projects/spectator/internals/src/public_api.ts', 19 | '@ngneat/spectator/jest': '/projects/spectator/jest/src/public_api.ts', 20 | '@ngneat/spectator': '/projects/spectator/src/public_api.ts', 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /projects/spectator/jest/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/spectator/jest/src/lib/dom-selectors.ts: -------------------------------------------------------------------------------- 1 | export { byAltText, byLabel, byPlaceholder, byText, byTextContent, byTitle, byValue, byTestId, byRole } from '@ngneat/spectator'; 2 | -------------------------------------------------------------------------------- /projects/spectator/jest/src/lib/spectator-http.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createHttpFactory as baseCreateHttpFactory, 4 | isType, 5 | CreateHttpOverrides, 6 | HttpMethod, 7 | SpectatorHttp as BaseSpectatorHttp, 8 | SpectatorHttpOptions, 9 | Token, 10 | } from '@ngneat/spectator'; 11 | 12 | import { mockProvider, SpyObject } from './mock'; 13 | 14 | /** 15 | * @publicApi 16 | */ 17 | export interface SpectatorHttp extends BaseSpectatorHttp { 18 | inject(token: Token): SpyObject; 19 | } 20 | 21 | /** 22 | * @publicApi 23 | */ 24 | export { HttpMethod }; 25 | 26 | /** 27 | * @pubicApi 28 | */ 29 | export type SpectatorHttpFactory = (overrides?: CreateHttpOverrides) => SpectatorHttp; 30 | 31 | /** 32 | * @publicApi 33 | */ 34 | export function createHttpFactory(typeOrOptions: SpectatorHttpOptions | Type): SpectatorHttpFactory { 35 | return baseCreateHttpFactory({ 36 | mockProvider, 37 | ...(isType(typeOrOptions) ? { service: typeOrOptions } : typeOrOptions), 38 | }) as SpectatorHttpFactory; 39 | } 40 | -------------------------------------------------------------------------------- /projects/spectator/jest/src/lib/spectator-injection-context.ts: -------------------------------------------------------------------------------- 1 | import { AbstractType, InjectionToken, Type } from '@angular/core'; 2 | import { 3 | createInjectionContextFactory as baseInjectionContextFactory, 4 | SpectatorInjectionContextOverrides, 5 | SpectatorInjectionContextOptions, 6 | SpectatorInjectionContext as BaseSpectatorInjectionContext, 7 | } from '@ngneat/spectator'; 8 | import { mockProvider, SpyObject } from './mock'; 9 | 10 | /** 11 | * @publicApi 12 | */ 13 | export interface SpectatorInjectionContext extends BaseSpectatorInjectionContext { 14 | inject(token: Type | InjectionToken | AbstractType): SpyObject; 15 | } 16 | 17 | /** 18 | * @publicApi 19 | */ 20 | export type SpectatorInjectionContextFactory = (overrides?: SpectatorInjectionContextOverrides) => SpectatorInjectionContext; 21 | 22 | /** 23 | * @publicApi 24 | */ 25 | export function createInjectionContextFactory(options: SpectatorInjectionContextOptions): SpectatorInjectionContextFactory { 26 | return baseInjectionContextFactory({ 27 | mockProvider, 28 | ...options, 29 | }) as SpectatorInjectionContextFactory; 30 | } 31 | -------------------------------------------------------------------------------- /projects/spectator/jest/src/lib/spectator-pipe.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createPipeFactory as baseCreatePipeFactory, 4 | isType, 5 | HostComponent, 6 | SpectatorPipe as BaseSpectatorPipe, 7 | SpectatorPipeOptions, 8 | SpectatorPipeOverrides, 9 | Token, 10 | } from '@ngneat/spectator'; 11 | 12 | import { mockProvider, SpyObject } from './mock'; 13 | 14 | /** 15 | * @publicApi 16 | */ 17 | export class SpectatorPipe extends BaseSpectatorPipe { 18 | public inject(token: Token): SpyObject { 19 | return super.inject(token) as SpyObject; 20 | } 21 | } 22 | 23 | /** 24 | * @publicApi 25 | */ 26 | export type SpectatorPipeFactory = ( 27 | templateOrOverrides?: string | SpectatorPipeOverrides, 28 | overrides?: SpectatorPipeOverrides, 29 | ) => SpectatorPipe; 30 | 31 | /** 32 | * @publicApi 33 | */ 34 | export function createPipeFactory(typeOrOptions: Type

| SpectatorPipeOptions): SpectatorPipeFactory { 35 | return baseCreatePipeFactory({ 36 | mockProvider, 37 | ...(isType(typeOrOptions) ? { pipe: typeOrOptions } : typeOrOptions), 38 | }) as SpectatorPipeFactory; 39 | } 40 | -------------------------------------------------------------------------------- /projects/spectator/jest/src/lib/spectator-routing.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createRoutingFactory as baseCreateRoutingFactory, 4 | isType, 5 | SpectatorRouting as BaseSpectatorRouting, 6 | SpectatorRoutingOptions, 7 | SpectatorRoutingOverrides, 8 | Token, 9 | } from '@ngneat/spectator'; 10 | 11 | import { mockProvider, SpyObject } from './mock'; 12 | 13 | /** 14 | * @publicApi 15 | */ 16 | export class SpectatorRouting extends BaseSpectatorRouting { 17 | public inject(token: Token, fromComponentInjector: boolean = false): SpyObject { 18 | return super.inject(token, fromComponentInjector) as SpyObject; 19 | } 20 | } 21 | 22 | /** 23 | * @publicApi 24 | */ 25 | export type SpectatorRoutingFactory = (overrides?: SpectatorRoutingOverrides) => SpectatorRouting; 26 | 27 | /** 28 | * @publicApi 29 | */ 30 | export function createRoutingFactory(typeOrOptions: SpectatorRoutingOptions | Type): SpectatorRoutingFactory { 31 | return baseCreateRoutingFactory({ 32 | mockProvider, 33 | ...(isType(typeOrOptions) ? { component: typeOrOptions } : typeOrOptions), 34 | }) as SpectatorRoutingFactory; 35 | } 36 | -------------------------------------------------------------------------------- /projects/spectator/jest/src/lib/spectator-service.ts: -------------------------------------------------------------------------------- 1 | import { Type, InjectionToken, AbstractType } from '@angular/core'; 2 | import { 3 | createServiceFactory as baseCreateServiceFactory, 4 | isType, 5 | SpectatorServiceOverrides, 6 | SpectatorServiceOptions, 7 | SpectatorService as BaseSpectatorService, 8 | Token, 9 | } from '@ngneat/spectator'; 10 | 11 | import { mockProvider, SpyObject } from './mock'; 12 | 13 | /** 14 | * @publicApi 15 | */ 16 | export interface SpectatorService extends BaseSpectatorService { 17 | inject(token: Type | InjectionToken | AbstractType): SpyObject; 18 | } 19 | 20 | /** 21 | * @publicApi 22 | */ 23 | export type SpectatorServiceFactory = (overrides?: SpectatorServiceOverrides) => SpectatorService; 24 | 25 | /** 26 | * @publicApi 27 | */ 28 | export function createServiceFactory(typeOrOptions: SpectatorServiceOptions | Type): SpectatorServiceFactory { 29 | return baseCreateServiceFactory({ 30 | mockProvider, 31 | ...(isType(typeOrOptions) ? { service: typeOrOptions } : typeOrOptions), 32 | }) as SpectatorServiceFactory; 33 | } 34 | -------------------------------------------------------------------------------- /projects/spectator/jest/src/lib/spectator.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createComponentFactory as baseCreateComponentFactory, 4 | isType, 5 | Spectator as BaseSpectator, 6 | SpectatorOptions, 7 | SpectatorOverrides, 8 | Token, 9 | } from '@ngneat/spectator'; 10 | 11 | import { mockProvider, SpyObject } from './mock'; 12 | 13 | /** 14 | * @publicApi 15 | */ 16 | export type SpectatorFactory = (options?: SpectatorOverrides) => Spectator; 17 | 18 | export function createComponentFactory(typeOrOptions: SpectatorOptions | Type): SpectatorFactory { 19 | return baseCreateComponentFactory({ 20 | mockProvider, 21 | ...(isType(typeOrOptions) ? { component: typeOrOptions } : typeOrOptions), 22 | }) as SpectatorFactory; 23 | } 24 | 25 | export class Spectator extends BaseSpectator { 26 | public inject(token: Token, fromComponentInjector: boolean = false): SpyObject { 27 | return super.inject(token, fromComponentInjector) as SpyObject; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /projects/spectator/jest/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | export * from './lib/dom-selectors'; 4 | export * from './lib/mock'; 5 | export * from './lib/spectator'; 6 | export * from './lib/spectator-http'; 7 | export * from './lib/spectator-directive'; 8 | export * from './lib/spectator-service'; 9 | export * from './lib/spectator-host'; 10 | export * from './lib/spectator-routing'; 11 | export * from './lib/spectator-pipe'; 12 | export * from './lib/spectator-injection-context'; 13 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/async-input/async-input.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync } from '@angular/core/testing'; 2 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; 3 | 4 | import { AsyncInputComponent } from '../../../test/async-input/async-input.component'; 5 | 6 | describe('ZippyComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory(AsyncInputComponent); 10 | 11 | it('should work', () => { 12 | const { component } = createHost(``); 13 | expect(component).toBeDefined(); 14 | }); 15 | 16 | it('should not be visible', () => { 17 | host = createHost(``); 18 | host.setHostInput('widgets', ''); 19 | expect(host.query('div')).not.toExist(); 20 | }); 21 | 22 | it('should be visible', fakeAsync(() => { 23 | host = createHost(``, { 24 | detectChanges: true, 25 | hostProps: { 26 | widgets: '', 27 | }, 28 | }); 29 | host.tick(); 30 | host.detectChanges(); 31 | expect(host.query('div')).toExist(); 32 | })); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/async/async.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; 2 | 3 | import { AsyncComponent } from '../../../test/async/async.component'; 4 | import { QueryService } from '../../../test/query.service'; 5 | 6 | describe('ZippyComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory({ 10 | component: AsyncComponent, 11 | mocks: [QueryService], 12 | }); 13 | 14 | it('should work', () => { 15 | const { component } = createHost(``); 16 | expect(component).toBeDefined(); 17 | }); 18 | 19 | it('should be falsy', () => { 20 | host = createHost(``); 21 | expect(host.query('p')).not.toExist(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; 2 | 3 | import { AuthService } from '../../test/auth.service'; 4 | import { DateService } from '../../test/date.service'; 5 | 6 | describe('AuthService', () => { 7 | it('should ', () => { 8 | expect(true).toBeTruthy(); 9 | }); 10 | 11 | let spectator: SpectatorService; 12 | const createService = createServiceFactory({ 13 | service: AuthService, 14 | mocks: [DateService], 15 | }); 16 | 17 | beforeEach(() => (spectator = createService())); 18 | 19 | it('should not be logged in', () => { 20 | const dateService = spectator.inject(DateService); 21 | dateService.isExpired.mockReturnValue(true); 22 | expect(spectator.service.isLoggedIn()).toBeFalsy(); 23 | }); 24 | 25 | it('should be logged in', () => { 26 | const dateService = spectator.inject(DateService); 27 | dateService.isExpired.mockReturnValue(false); 28 | expect(spectator.service.isLoggedIn()).toBeTruthy(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/calc.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; 2 | 3 | import { CalcComponent } from '../../test/calc/calc.component'; 4 | 5 | describe('CalcComponent', () => { 6 | let spectator: Spectator; 7 | const createComponent = createComponentFactory(CalcComponent); 8 | 9 | it('should be defined', () => { 10 | spectator = createComponent(); 11 | expect(spectator.component).toBeTruthy(); 12 | }); 13 | 14 | it('should calc the value', () => { 15 | spectator = createComponent(); 16 | const a = spectator.query('.a') as HTMLInputElement; 17 | const b = spectator.query('.b') as HTMLInputElement; 18 | spectator.typeInElement('1', a); 19 | spectator.typeInElement('2', b); 20 | 21 | expect(spectator.query('.result')).toHaveText('12'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/click/click.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync } from '@angular/core/testing'; 2 | import { byText, createComponentFactory, Spectator } from '@ngneat/spectator/jest'; 3 | import { ClickComponent } from '../../../test/click/click.component'; 4 | 5 | describe('ClickComponent', () => { 6 | let component: ClickComponent; 7 | let spectator: Spectator; 8 | 9 | const createComponent = createComponentFactory(ClickComponent); 10 | 11 | beforeEach(() => { 12 | spectator = createComponent(); 13 | component = spectator.component; 14 | }); 15 | 16 | it('should create', () => { 17 | expect(component).toBeTruthy(); 18 | }); 19 | 20 | it('should changed on click with click query shorthand', fakeAsync(() => { 21 | spectator.click('button'); 22 | spectator.tick(100); 23 | 24 | expect('p').toHaveText('changed'); 25 | })); 26 | 27 | it('should changed on click with click dom selector', fakeAsync(() => { 28 | spectator.click(byText('Change')); 29 | spectator.tick(100); 30 | 31 | expect('p').toHaveText('changed'); 32 | })); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/consum-dynamic/consume-dynamic.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; 2 | 3 | import { ConsumeDynamicComponent } from '../../../test/consum-dynamic/consume-dynamic.component'; 4 | import { DynamicComponent } from '../../../test/dynamic/dynamic.component'; 5 | 6 | describe('ConsumeDynamicComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory({ 10 | declarations: [DynamicComponent], 11 | entryComponents: [DynamicComponent], 12 | component: ConsumeDynamicComponent, 13 | }); 14 | 15 | it('should work', () => { 16 | host = createHost(``); 17 | expect(host.component).toBeDefined(); 18 | }); 19 | 20 | it('should render the dynamic component', () => { 21 | host = createHost(``); 22 | expect(host.queryHost('.dynamic')).toHaveText('dynamic works!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/consumer.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/jest'; 2 | import { Subject } from 'rxjs'; 3 | 4 | import { ConsumerService } from '../../test/consumer.service'; 5 | import { ProviderService } from '../../test/provider.service'; 6 | 7 | describe('ConsumerService', () => { 8 | const randomNumber = Math.random(); 9 | 10 | let spectator: SpectatorService; 11 | const createService = createServiceFactory({ 12 | service: ConsumerService, 13 | providers: [ 14 | mockProvider(ProviderService, { 15 | obs$: new Subject(), 16 | method: () => randomNumber, 17 | }), 18 | ], 19 | }); 20 | 21 | beforeEach(() => (spectator = createService())); 22 | 23 | it('should consume mocked service with properties', () => { 24 | const provider = spectator.inject(ProviderService); 25 | expect(spectator.service.lastValue).toBeUndefined(); 26 | provider.obs$.next('hey you'); 27 | expect(spectator.service.lastValue).toBe('hey you'); 28 | }); 29 | 30 | it('should consume mocked service methods', () => { 31 | expect(spectator.service.consumeProvider()).toBe(randomNumber); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/fg/fg.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { SpectatorHost, createHostFactory } from '@ngneat/spectator/jest'; 2 | import { Component } from '@angular/core'; 3 | import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; 4 | 5 | import { FgComponent } from '../../../test/fg/fg.component'; 6 | 7 | @Component({ 8 | selector: 'app-custom-host', 9 | template: '', 10 | standalone: false, 11 | }) 12 | class CustomHostComponent { 13 | public group = new FormGroup({ 14 | name: new FormControl('name'), 15 | }); 16 | } 17 | 18 | describe('With Custom Host Component', () => { 19 | let host: SpectatorHost; 20 | 21 | const createHost = createHostFactory({ 22 | component: FgComponent, 23 | imports: [ReactiveFormsModule], 24 | host: CustomHostComponent, 25 | }); 26 | 27 | it('should display the host component title', () => { 28 | host = createHost(``); 29 | expect(host.component).toBeDefined(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/form-select/form-select.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFormsModule } from '@angular/forms'; 2 | import { Spectator, createComponentFactory } from '@ngneat/spectator/jest'; 3 | 4 | import { FormSelectComponent } from './form-select.component'; 5 | 6 | describe('FormSelectComponent', () => { 7 | let spectator: Spectator; 8 | 9 | const createComponent = createComponentFactory({ 10 | component: FormSelectComponent, 11 | imports: [ReactiveFormsModule], 12 | }); 13 | 14 | beforeEach(() => (spectator = createComponent())); 15 | 16 | it('should set the correct option on standard select', () => { 17 | // const select = spectator.query('#test-single-select') as HTMLSelectElement; 18 | // spectator.selectOption(select, '1'); 19 | // expect(select).toHaveSelectedOptions('1'); 20 | expect(true).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/form-select/form-select.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ChangeDetectionStrategy } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-form-select', 5 | template: ` 6 | 11 | `, 12 | changeDetection: ChangeDetectionStrategy.OnPush, 13 | standalone: false, 14 | }) 15 | export class FormSelectComponent { 16 | /** 17 | * Empty method to spy on 18 | */ 19 | public handleChange(): void { 20 | return; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/mock.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockProvider } from '@ngneat/spectator/jest'; 2 | 3 | import { WidgetService } from '../../test/widget.service'; 4 | 5 | describe('mockProvider', () => { 6 | it('should not modify the object passed in 2nd argument when running the mock factory', () => { 7 | const customPropertiesAndMethods: Partial> = { 8 | testingProperty: 'overriden', 9 | }; 10 | const { useFactory: factory } = mockProvider(WidgetService, customPropertiesAndMethods); 11 | factory(); 12 | expect(customPropertiesAndMethods).toEqual({ 13 | testingProperty: 'overriden', 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/ngonchanges-input/ngonchanges-input.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; 2 | import { NgOnChangesInputComponent } from '../../../test/ngonchanges-input/ngonchanges-input.component'; 3 | 4 | describe('NgOnChangesInputComponent', () => { 5 | describe('with Spectator', () => { 6 | let spectator: Spectator; 7 | 8 | const createComponent = createComponentFactory({ 9 | component: NgOnChangesInputComponent, 10 | }); 11 | 12 | beforeEach(() => { 13 | spectator = createComponent(); 14 | }); 15 | 16 | it('should re-render when updating fields in ngOnChanges', () => { 17 | expect(spectator.query('button')).toBeDisabled(); 18 | expect(spectator.query('button')).toHaveText('Button disabled'); 19 | 20 | spectator.setInput({ btnDisabled: false }); 21 | expect(spectator.query('button')).not.toBeDisabled(); 22 | expect(spectator.query('button')).toHaveText('Button enabled'); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/run-in-injection-context.spec.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable, InjectionToken, NgModule } from '@angular/core'; 2 | import { createInjectionContextFactory, SpectatorInjectionContext } from '@ngneat/spectator/jest'; 3 | 4 | const TEST_TOKEN = new InjectionToken('simple-token'); 5 | 6 | @Injectable() 7 | export class TestService { 8 | flag = false; 9 | } 10 | 11 | @NgModule({ 12 | providers: [TestService], 13 | }) 14 | export class TestModule {} 15 | 16 | const testFn = (arg: any) => { 17 | const token = inject(TEST_TOKEN); 18 | const { flag } = inject(TestService); 19 | 20 | return { token, flag, arg }; 21 | }; 22 | 23 | describe('Run in injection context', () => { 24 | describe('with Spectator', () => { 25 | const createContext = createInjectionContextFactory({ imports: [TestModule], providers: [{ provide: TEST_TOKEN, useValue: 'abcd' }] }); 26 | 27 | let spectator: SpectatorInjectionContext; 28 | 29 | beforeEach(() => (spectator = createContext())); 30 | 31 | it('should execute fn in injection context', () => { 32 | const service = spectator.inject(TestService); 33 | service.flag = true; 34 | 35 | const result = spectator.runInInjectionContext(() => testFn(2)); 36 | expect(result).toEqual({ token: 'abcd', flag: true, arg: 2 }); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/spy-object/spy-object.spec.ts: -------------------------------------------------------------------------------- 1 | import { createSpyObject } from '@ngneat/spectator/jest'; 2 | 3 | import { Person } from '../../../test/spy-object/person'; 4 | 5 | describe('SpyObject', () => { 6 | it('should mock all public methods', () => { 7 | const person = createSpyObject(Person); 8 | 9 | person.sayHi.andReturn('Bye!'); 10 | }); 11 | 12 | it('should enable spying on properties', () => { 13 | const person = createSpyObject(Person); 14 | person.birthYear = 1990; 15 | jest.spyOn(person, 'age', 'get').mockReturnValue(29); 16 | 17 | expect(person.age).toBe(29); 18 | }); 19 | 20 | it('should enable setting properties by just assigning', () => { 21 | const person = createSpyObject(Person); 22 | person.birthYear = 1990; 23 | (person as any).age = 29; 24 | 25 | expect(person.age).toBe(29); 26 | }); 27 | 28 | it('should allow setting properties', () => { 29 | const person = createSpyObject(Person); 30 | 31 | person.birthYear = 1995; // should compile 32 | }); 33 | 34 | it('should allow setting readonly properties with cast method', () => { 35 | const person = createSpyObject(Person); 36 | 37 | person.castToWritable().name = 'Other name'; // should compile 38 | 39 | expect(person.name).toBe('Other name'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/standalone/component/standalone.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, createHostFactory, Spectator, SpectatorHost } from '@ngneat/spectator/jest'; 2 | import { StandaloneComponent } from '../../../../test/standalone/component/standalone.component'; 3 | 4 | describe('StandaloneComponent', () => { 5 | describe('with Spectator', () => { 6 | let spectator: Spectator; 7 | 8 | const createComponent = createComponentFactory({ 9 | component: StandaloneComponent, 10 | }); 11 | 12 | beforeEach(() => { 13 | spectator = createComponent(); 14 | }); 15 | 16 | it('should render a StandaloneComponent', () => { 17 | expect(spectator.query('#standalone')).toContainText('This stands alone!'); 18 | }); 19 | }); 20 | 21 | describe('with SpectatorHost', () => { 22 | let host: SpectatorHost; 23 | 24 | const createHost = createHostFactory({ 25 | component: StandaloneComponent, 26 | template: `

`, 27 | }); 28 | 29 | beforeEach(() => { 30 | host = createHost(); 31 | }); 32 | 33 | it('should render a StandaloneComponent', () => { 34 | expect(host.query('#standalone')).toContainText('This stands alone!'); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/standalone/directive/standalone.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/jest'; 2 | import { StandaloneDirective } from '../../../../test/standalone/directive/standalone.directive'; 3 | 4 | describe('StandaloneDirective', () => { 5 | describe('with SpectatorDirective', () => { 6 | let spectator: SpectatorDirective; 7 | 8 | const createDirective = createDirectiveFactory({ 9 | directive: StandaloneDirective, 10 | template: `
This stands alone!
`, 11 | }); 12 | 13 | beforeEach(() => { 14 | spectator = createDirective(); 15 | }); 16 | 17 | it('should render a StandaloneDirective', () => { 18 | expect(spectator.query("[class='btn']")).toContainText('This stands alone!'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/teardown/error.ts: -------------------------------------------------------------------------------- 1 | export class TeardownError extends Error { 2 | message = 'The error which is thrown during teardown'; 3 | } 4 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/teardown/teardown.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnDestroy } from '@angular/core'; 2 | 3 | import { TeardownError } from './error'; 4 | import { TeardownService } from './teardown.service'; 5 | 6 | @Component({ 7 | selector: 'app-teardown', 8 | template: '', 9 | standalone: false, 10 | }) 11 | export class TeardownComponent implements OnDestroy { 12 | @Input() 13 | rethrowErrors = false; 14 | 15 | constructor(readonly teardownService: TeardownService) {} 16 | 17 | ngOnDestroy(): void { 18 | if (this.rethrowErrors) { 19 | throw new TeardownError(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/teardown/teardown.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnDestroy } from '@angular/core'; 2 | 3 | @Injectable({ providedIn: 'root' }) 4 | export class TeardownService implements OnDestroy { 5 | ngOnDestroy(): void {} 6 | } 7 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/unless/unless.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; 2 | 3 | import { AppUnlessDirective } from '../../../test/unless/unless.component'; 4 | 5 | describe('HelloComponent', () => { 6 | let host: SpectatorHost; 7 | 8 | const createHost = createHostFactory(AppUnlessDirective); 9 | 10 | it('should work', () => { 11 | host = createHost(`
Hello world
`); 12 | expect(host.hostElement).toHaveText('Hello world'); 13 | }); 14 | 15 | it('should work', () => { 16 | host = createHost(`
Hello world
`); 17 | expect(host.hostElement).not.toHaveText('Hello world'); 18 | }); 19 | 20 | it('should use hostElement when using query to find element', () => { 21 | host = createHost(`
Hello world
`); 22 | expect(host.query('div')).not.toHaveText('Hello world'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/widget.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; 2 | 3 | import { WidgetDataService } from '../../test/widget-data.service'; 4 | import { WidgetService } from '../../test/widget.service'; 5 | 6 | describe('WidgetService', () => { 7 | let spectator: SpectatorService; 8 | const createService = createServiceFactory({ 9 | service: WidgetService, 10 | mocks: [WidgetDataService], 11 | }); 12 | 13 | beforeEach(() => (spectator = createService())); 14 | 15 | it('should be defined', () => { 16 | expect(spectator.service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /projects/spectator/jest/test/widget/widget.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; 2 | 3 | import { WidgetComponent } from '../../../test/widget/widget.component'; 4 | import { WidgetService } from '../../../test/widget.service'; 5 | 6 | describe('WidgetComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory({ 10 | component: WidgetComponent, 11 | mocks: [WidgetService], 12 | }); 13 | 14 | it('should work', () => { 15 | host = createHost(``); 16 | expect(host.component).toBeDefined(); 17 | }); 18 | 19 | it('should call the service method on button click', () => { 20 | host = createHost(``); 21 | host.click('button'); 22 | const widgetService = host.component.widgetService; 23 | expect(widgetService.get).toHaveBeenCalled(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/jest/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.spec.json", 3 | "include": [ 4 | "test/**/*.spec.ts", 5 | "../src/lib/matchers-types.ts" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /projects/spectator/karma.conf.js: -------------------------------------------------------------------------------- 1 | // tslint:disable 2 | 3 | // Karma configuration file, see link for more information 4 | // https://karma-runner.github.io/1.0/config/configuration-file.html 5 | 6 | const build = process.env.NODE_ENV === 'build'; 7 | 8 | module.exports = function(config) { 9 | config.set({ 10 | basePath: "", 11 | frameworks: ["jasmine", "@angular-devkit/build-angular"], 12 | plugins: [ 13 | require("karma-jasmine"), 14 | require("karma-chrome-launcher"), 15 | require("karma-jasmine-html-reporter"), 16 | require("karma-coverage-istanbul-reporter"), 17 | require("@angular-devkit/build-angular/plugins/karma"), 18 | ], 19 | client: { 20 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 21 | }, 22 | coverageIstanbulReporter: { 23 | dir: require("path").join(__dirname, "../../coverage/spectator"), 24 | reports: ["html", "lcovonly"], 25 | fixWebpackSourcePaths: true, 26 | }, 27 | reporters: ["progress", "kjhtml"], 28 | port: 9876, 29 | colors: true, 30 | logLevel: config.LOG_INFO, 31 | autoWatch: true, 32 | browsers: [build ? 'ChromeHeadless' : 'Chrome'], 33 | singleRun: build, 34 | restartOnFileChange: true 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /projects/spectator/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/spectator", 4 | "lib": { 5 | "entryFile": "src/public_api.ts" 6 | }, 7 | "allowedNonPeerDependencies": [ 8 | "@testing-library/dom", 9 | "jquery", 10 | "@types/jasmine", 11 | "replace-in-file" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /projects/spectator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngneat/spectator", 3 | "description": "A powerful tool to simplify your Angular tests", 4 | "author": "Netanel Basal ", 5 | "version": "19.6.2", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/ngneat/spectator" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/ngneat/spectator/issues" 13 | }, 14 | "homepage": "https://github.com/ngneat/spectator#readme", 15 | "keywords": [ 16 | "angular unit test", 17 | "angular simplified tests", 18 | "jasmine", 19 | "angular", 20 | "angular tests", 21 | "angular easy tests", 22 | "angular test services", 23 | "angular testing dumb components" 24 | ], 25 | "dependencies": { 26 | "@testing-library/dom": "^8.11.0", 27 | "jquery": "^3.7.1", 28 | "replace-in-file": "6.2.0", 29 | "tslib": "^2.6.2" 30 | }, 31 | "peerDependencies": { 32 | "@angular/common": ">= 18.0.0", 33 | "@angular/router": ">= 18.0.0", 34 | "@angular/animations": ">= 18.0.0" 35 | }, 36 | "schematics": "./schematics/collection.json" 37 | } 38 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json", 3 | "extends": ["@schematics/angular"], 4 | "schematics": { 5 | "spectator-component": { 6 | "description": "Spectator component", 7 | "factory": "./spectator/index#spectatorComponentSchematic", 8 | "schema": "./spectator/component-schema.json", 9 | "aliases": ["cs"] 10 | }, 11 | "spectator-service": { 12 | "description": "Spectator service", 13 | "factory": "./spectator/index#spectatorServiceSchematic", 14 | "schema": "./spectator/service-schema.json", 15 | "aliases": ["ss"] 16 | }, 17 | "spectator-directive": { 18 | "description": "Spectator directive", 19 | "factory": "./spectator/index#spectatorDirectiveSchematic", 20 | "schema": "./spectator/directive-schema.json", 21 | "aliases": ["ds"] 22 | }, 23 | "spectator-pipe": { 24 | "description": "Spectator pipe", 25 | "factory": "./spectator/index#spectatorPipeSchematic", 26 | "schema": "./spectator/pipe-schema.json", 27 | "aliases": ["ps"] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/files/component-custom-host/__name@dasherize__.__type@dasherize__.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator<% if (secondaryEntryPoint) { %>/<%= secondaryEntryPoint%><% } %>'; 3 | 4 | import { <%= classify(name)%>Component } from './<%= dasherize(name)%>.component'; 5 | 6 | @Component({ 7 | template: '', 8 | standalone: false 9 | }) 10 | class CustomHostComponent { 11 | title = 'Custom HostComponent'; 12 | } 13 | 14 | describe('<%= classify(name)%>Component', () => { 15 | let host: SpectatorHost<<%= classify(name)%>Component, CustomHostComponent>; 16 | const createHost = createHostFactory({ 17 | component: <%= classify(name)%>Component, 18 | host: CustomHostComponent 19 | }); 20 | 21 | it('should display the host component title', () => { 22 | host = createHost(``); 23 | expect(host.query('.zippy__title')).toHaveText('Custom HostComponent'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/files/component-host/__name@dasherize__.__type@dasherize__.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator<% if (secondaryEntryPoint) { %>/<%= secondaryEntryPoint%><% } %>'; 2 | 3 | import { <%= classify(name)%>Component } from './<%= dasherize(name)%>.component'; 4 | 5 | describe('<%= classify(name)%>Component', () => { 6 | let spectator: SpectatorHost<<%= classify(name)%>Component>; 7 | 8 | const createHost = createHostFactory(<%= classify(name)%>Component); 9 | 10 | beforeEach(() => { 11 | spectator = createHost(` title="Zippy title">Zippy content>`); 12 | }); 13 | 14 | it('should create', () => { 15 | expect(spectator.component).toBeTruthy(); 16 | }); 17 | 18 | it('should...', () => { 19 | spectator.click('.zippy__title'); 20 | expect(spectator.query('.arrow')).toHaveText('Close'); 21 | }); 22 | 23 | it('should...', () => { 24 | spectator.click('.zippy__title'); 25 | expect(spectator.query('.zippy__content')).toExist(); 26 | spectator.click('.zippy__title'); 27 | expect('.zippy__content').not.toExist(); 28 | }); 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/files/component/__name@dasherize__.__type@dasherize__.spec.ts: -------------------------------------------------------------------------------- 1 | import { Spectator, createComponentFactory } from '@ngneat/spectator<% if (secondaryEntryPoint) { %>/<%= secondaryEntryPoint%><% } %>'; 2 | 3 | import { <%= classify(name)%>Component } from './<%= dasherize(name)%>.component'; 4 | 5 | describe('<%= classify(name)%>Component', () => { 6 | let spectator: Spectator<<%= classify(name)%>Component>; 7 | const createComponent = createComponentFactory(<%= classify(name)%>Component); 8 | 9 | it('should create', () => { 10 | spectator = createComponent(); 11 | 12 | expect(spectator.component).toBeTruthy(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/files/data-service/__name@dasherize__.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHttpFactory, HttpMethod, SpectatorHttp } from '@ngneat/spectator<% if (secondaryEntryPoint) { %>/<%= secondaryEntryPoint%><% } %>'; 2 | import { <%= classify(name)%>Service } from './<%= dasherize(name)%>.service'; 3 | 4 | describe('<%= classify(name)%>Service', () => { 5 | let spectator: SpectatorHttp<<%= classify(name)%>Service>; 6 | const createHttp = createHttpFactory(<%= classify(name)%>Service); 7 | 8 | beforeEach(() => spectator = createHttp()); 9 | 10 | it('can test HttpClient.get', () => { 11 | // spectator.service.getTodos().subscribe(); 12 | // spectator.expectOne('api/todos', HttpMethod.GET); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/files/directive/__name@dasherize__.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator<% if (secondaryEntryPoint) { %>/<%= secondaryEntryPoint%><% } %>'; 2 | 3 | import { <%= classify(name)%>Directive } from './<%= dasherize(name)%>.directive'; 4 | 5 | describe('<%= classify(name)%>Directive', () => { 6 | let spectator: SpectatorDirective<<%= classify(name)%>Directive>; 7 | const createDirective = createDirectiveFactory(<%= classify(name)%>Directive); 8 | 9 | it('should change the background color', () => { 10 | spectator = createDirective(`
Testing <%= classify(name)%>Directive
`); 11 | 12 | spectator.dispatchMouseEvent(spectator.element, 'mouseover'); 13 | 14 | expect(spectator.element).toHaveStyle({ 15 | backgroundColor: 'rgba(0,0,0, 0.1)' 16 | }); 17 | 18 | spectator.dispatchMouseEvent(spectator.element, 'mouseout'); 19 | expect(spectator.element).toHaveStyle({ 20 | backgroundColor: '#fff' 21 | }); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/files/pipe/__name@dasherize__.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { createPipeFactory, SpectatorPipe } from '@ngneat/spectator<% if (secondaryEntryPoint) { %>/<%= secondaryEntryPoint%><% } %>'; 2 | 3 | import { <%= classify(name)%>Pipe } from './<%= dasherize(name)%>.pipe'; 4 | 5 | describe('<%= classify(name)%>Pipe ', () => { 6 | let spectator: SpectatorPipe<<%= classify(name)%>Pipe>; 7 | const createPipe = createPipeFactory(<%= classify(name)%>Pipe); 8 | 9 | it('should change the background color', () => { 10 | spectator = createPipe(`
{{ 'Testing' | <%= camelize(name)%> }}
`); 11 | 12 | expect(spectator.element).toHaveText('Testing'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/files/service/__name@dasherize__.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, SpectatorService } from '@ngneat/spectator<% if (secondaryEntryPoint) { %>/<%= secondaryEntryPoint%><% } %>'; 2 | import { <%= classify(name)%>Service } from './<%= dasherize(name)%>.service'; 3 | 4 | describe('<%= classify(name)%>Service', () => { 5 | let spectator: SpectatorService<<%= classify(name)%>Service>; 6 | const createService = createServiceFactory(<%= classify(name)%>Service); 7 | 8 | beforeEach(() => spectator = createService()); 9 | 10 | it('should...', () => { 11 | expect(spectator.service).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/schema.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.PipeOptions = exports.DirectiveOptions = exports.ServiceOptions = exports.ComponentOptions = void 0; 4 | class ComponentOptions { 5 | } 6 | exports.ComponentOptions = ComponentOptions; 7 | class ServiceOptions { 8 | } 9 | exports.ServiceOptions = ServiceOptions; 10 | class DirectiveOptions { 11 | } 12 | exports.DirectiveOptions = DirectiveOptions; 13 | class PipeOptions { 14 | } 15 | exports.PipeOptions = PipeOptions; 16 | //# sourceMappingURL=schema.js.map -------------------------------------------------------------------------------- /projects/spectator/schematics/src/spectator/schema.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"schema.js","sourceRoot":"","sources":["schema.ts"],"names":[],"mappings":";;;AAIA,MAAa,gBAAgB;CA8D5B;AA9DD,4CA8DC;AACD,MAAa,cAAc;CAqB1B;AArBD,wCAqBC;AACD,MAAa,gBAAgB;CAwC5B;AAxCD,4CAwCC;AACD,MAAa,WAAW;CAoCvB;AApCD,kCAoCC"} -------------------------------------------------------------------------------- /projects/spectator/schematics/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "tsconfig", 4 | "lib": ["es2017", "dom"], 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "noEmitOnError": true, 8 | "noFallthroughCasesInSwitch": true, 9 | "noImplicitAny": true, 10 | "noImplicitThis": true, 11 | "noUnusedParameters": true, 12 | "noUnusedLocals": true, 13 | "rootDir": "src/", 14 | "skipDefaultLibCheck": true, 15 | "skipLibCheck": true, 16 | "sourceMap": true, 17 | "strictNullChecks": true, 18 | "target": "ES2020", 19 | "types": ["jasmine", "node"] 20 | }, 21 | "include": ["src/**/*"], 22 | "exclude": ["src/*/files/**/*"] 23 | } 24 | -------------------------------------------------------------------------------- /projects/spectator/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import { defineGlobalsInjections } from './src/lib/globals-injections'; 2 | import { TranslatePipe } from './test/translate.pipe'; 3 | import { TranslateService } from './test/translate.service'; 4 | 5 | defineGlobalsInjections({ 6 | providers: [TranslateService], 7 | declarations: [TranslatePipe], 8 | }); 9 | -------------------------------------------------------------------------------- /projects/spectator/setup-vitest.ts: -------------------------------------------------------------------------------- 1 | import '@analogjs/vitest-angular/setup-zone'; 2 | 3 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { defineGlobalsInjections } from '@ngneat/spectator'; 6 | import { TranslateService } from './test/translate.service'; 7 | import { TranslatePipe } from './test/translate.pipe'; 8 | import { vi } from 'vitest'; 9 | 10 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 11 | 12 | defineGlobalsInjections({ 13 | providers: [TranslateService], 14 | declarations: [TranslatePipe], 15 | }); 16 | 17 | beforeEach(() => { 18 | const mockIntersectionObserver = vi.fn(); 19 | mockIntersectionObserver.mockReturnValue({ 20 | observe: () => null, 21 | unobserve: () => null, 22 | disconnect: () => null, 23 | }); 24 | window.IntersectionObserver = mockIntersectionObserver; 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/base/base-spectator.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SpyObject } from '../mock'; 4 | import { Token } from '../token'; 5 | 6 | /** 7 | * @internal 8 | */ 9 | export abstract class BaseSpectator { 10 | public inject(token: Token): SpyObject { 11 | return TestBed.inject ? TestBed.inject(token) : TestBed.get(token); 12 | } 13 | 14 | /** 15 | * Execute any pending effects. 16 | */ 17 | public flushEffects(): void { 18 | TestBed.flushEffects(); 19 | } 20 | 21 | public runInInjectionContext(fn: () => T): T { 22 | return TestBed.runInInjectionContext(fn); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/core.ts: -------------------------------------------------------------------------------- 1 | import { CustomMatcherFactory } from './matchers'; 2 | 3 | declare var jasmine: any; 4 | 5 | export function addMatchers(matchers: Record): void { 6 | if (!matchers) return; 7 | 8 | if (typeof jasmine !== 'undefined') { 9 | jasmine.addMatchers(matchers); 10 | } else { 11 | // Jest (when using ESM) and Vitest aren't on the global scope so we 12 | // assume that it's Jest or Vitest if Jasmine is not defined 13 | const jestVitestExpectExtend = {}; 14 | for (const key of Object.keys(matchers)) { 15 | if (key.startsWith('to')) jestVitestExpectExtend[key] = matchers[key]().compare; 16 | } 17 | 18 | (expect as any).extend(jestVitestExpectExtend); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/globals-injections.ts: -------------------------------------------------------------------------------- 1 | import { TestModuleMetadata } from '@angular/core/testing'; 2 | 3 | let globals: TestModuleMetadata = { 4 | providers: [], 5 | declarations: [], 6 | imports: [], 7 | }; 8 | 9 | export function defineGlobalsInjections(config: TestModuleMetadata): void { 10 | globals = { ...globals, ...config }; 11 | } 12 | 13 | export function getGlobalsInjections(): TestModuleMetadata { 14 | return globals; 15 | } 16 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/internals/merge.ts: -------------------------------------------------------------------------------- 1 | import { OptionalsRequired } from '../types'; 2 | 3 | /** 4 | * @internal 5 | */ 6 | export function merge(defaults: OptionalsRequired, overrides?: T): Required { 7 | return { ...defaults, ...overrides } as Required; 8 | } 9 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/internals/node-by-directive.ts: -------------------------------------------------------------------------------- 1 | import { DebugNode, Predicate, Type } from '@angular/core'; 2 | 3 | // TODO (dirkluijk): remove after upgrading to Angular 8.2 4 | // see: https://github.com/angular/angular/commit/10a1e1974b816ebb979dc10586b160ee07ad8356 5 | export function nodeByDirective(type: Type): Predicate { 6 | return (debugNode) => debugNode.providerTokens.includes(type); 7 | } 8 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/internals/rgb-to-hex.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Netanel Basal. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://github.com/NetanelBasal/spectator/blob/master/LICENSE 7 | */ 8 | 9 | export function hex2rgb(hex: string): string { 10 | const h = hex.replace('#', ''); 11 | const matches = h.match(new RegExp('(.{' + h.length / 3 + '})', 'g'))!; 12 | const [r, g, b] = matches.map((match) => parseInt(match.length === 1 ? match + match : match, 16)); 13 | 14 | return `rgb(${r},${g},${b})`; 15 | } 16 | 17 | export function isHex(value: string): boolean { 18 | return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value); 19 | } 20 | 21 | export function trim(value: string): string { 22 | return (value || '').replace(/\s/g, ''); 23 | } 24 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-directive/initial-module.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 2 | 3 | import { initialModule, ModuleMetadata } from '../base/initial-module'; 4 | import { declareInModule } from '../utils'; 5 | import { SpectatorDirectiveOptions } from './options'; 6 | 7 | /** 8 | * @internal 9 | */ 10 | export function initialSpectatorDirectiveModule(options: Required>): ModuleMetadata { 11 | const moduleMetadata = initialModule(options); 12 | 13 | if (options.declareDirective) { 14 | declareInModule(moduleMetadata, options.directive); 15 | } 16 | moduleMetadata.declarations.push(options.host); 17 | 18 | moduleMetadata.schemas = [options.shallow ? NO_ERRORS_SCHEMA : options.schemas || []]; 19 | 20 | return moduleMetadata; 21 | } 22 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-directive/options.ts: -------------------------------------------------------------------------------- 1 | import { Provider, Type } from '@angular/core'; 2 | 3 | import { BaseSpectatorOptions, getDefaultBaseOptions } from '../base/options'; 4 | import { merge } from '../internals/merge'; 5 | import { HostComponent } from '../spectator-host/host-component'; 6 | import { OptionalsRequired } from '../types'; 7 | 8 | /** 9 | * @publicApi 10 | */ 11 | export interface SpectatorDirectiveOptions extends BaseSpectatorOptions { 12 | directive: Type; 13 | shallow?: boolean; 14 | detectChanges?: boolean; 15 | host?: Type; 16 | template?: string; 17 | directiveProviders?: Provider[]; 18 | directiveMocks?: Type[]; 19 | declareDirective?: boolean; 20 | } 21 | 22 | const defaultSpectatorRoutingOptions: OptionalsRequired> = { 23 | ...getDefaultBaseOptions(), 24 | host: HostComponent, 25 | template: '', 26 | shallow: false, 27 | detectChanges: true, 28 | directiveProviders: [], 29 | directiveMocks: [], 30 | declareDirective: true, 31 | }; 32 | 33 | /** 34 | * @internal 35 | */ 36 | export function getSpectatorDirectiveDefaultOptions( 37 | overrides?: SpectatorDirectiveOptions, 38 | ): Required> { 39 | return merge(defaultSpectatorRoutingOptions, overrides); 40 | } 41 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-host/host-component.ts: -------------------------------------------------------------------------------- 1 | import { Component, NgModule } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'lib-ngneat-host-component', 5 | template: '', 6 | standalone: false, 7 | }) 8 | export class HostComponent {} 9 | 10 | /* 11 | This is an unused module to resolve the ng build error: 12 | 'Cannot determine the module for class HostComponent' 13 | 14 | Reference: https://github.com/angular/issues/13590 15 | */ 16 | @NgModule({ 17 | declarations: [HostComponent], 18 | }) 19 | export class HostModule {} 20 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-host/initial-module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleMetadata } from '../base/initial-module'; 2 | import { initialSpectatorModule } from '../spectator/initial-module'; 3 | 4 | import { SpectatorHostOptions } from './options'; 5 | 6 | /** 7 | * @internal 8 | */ 9 | export function initialSpectatorWithHostModule(options: Required>): ModuleMetadata { 10 | const moduleMetadata = initialSpectatorModule(options); 11 | 12 | moduleMetadata.declarations.push(options.host); 13 | 14 | return moduleMetadata; 15 | } 16 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-host/options.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { merge } from '../internals/merge'; 4 | import { getSpectatorDefaultOptions, SpectatorOptions } from '../spectator/options'; 5 | import { OptionalsRequired } from '../types'; 6 | 7 | import { HostComponent } from './host-component'; 8 | 9 | /** 10 | * @publicApi 11 | */ 12 | export interface SpectatorHostOptions extends SpectatorOptions { 13 | host?: Type; 14 | template?: string; 15 | } 16 | 17 | const defaultSpectatorHostOptions: OptionalsRequired> = { 18 | ...getSpectatorDefaultOptions(), 19 | host: HostComponent, 20 | template: '', 21 | }; 22 | 23 | /** 24 | * @internal 25 | */ 26 | export function getSpectatorHostDefaultOptions(overrides?: SpectatorHostOptions): Required> { 27 | return merge(defaultSpectatorHostOptions, overrides); 28 | } 29 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-http/initial-module.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 2 | 3 | import { initialModule, ModuleMetadata } from '../base/initial-module'; 4 | 5 | import { SpectatorHttpOptions } from './options'; 6 | 7 | /** 8 | * @internal 9 | */ 10 | export function initialHttpModule(options: Required>): ModuleMetadata { 11 | const moduleMetadata = initialModule(options); 12 | 13 | moduleMetadata.providers.push(options.service); 14 | moduleMetadata.imports.push(HttpClientTestingModule); 15 | 16 | return moduleMetadata; 17 | } 18 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-http/options.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { getDefaultBaseOptions, BaseSpectatorOptions } from '../base/options'; 4 | import { merge } from '../internals/merge'; 5 | import { OptionalsRequired } from '../types'; 6 | 7 | export interface SpectatorHttpOptions extends BaseSpectatorOptions { 8 | service: Type; 9 | } 10 | 11 | const defaultHttpOptions: OptionalsRequired> = { 12 | ...getDefaultBaseOptions(), 13 | }; 14 | 15 | /** 16 | * @internal 17 | */ 18 | export function getDefaultHttpOptions(overrides: SpectatorHttpOptions): Required> { 19 | return merge(defaultHttpOptions, overrides); 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-injection-context/initial-module.ts: -------------------------------------------------------------------------------- 1 | import { initialModule, ModuleMetadata } from '../base/initial-module'; 2 | import { FullInjectionContextOptions } from './options'; 3 | 4 | /** 5 | * @internal 6 | */ 7 | export function initialInjectionContextModule(options: FullInjectionContextOptions): ModuleMetadata { 8 | const moduleMetadata = initialModule(options); 9 | 10 | return moduleMetadata; 11 | } 12 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-injection-context/options.ts: -------------------------------------------------------------------------------- 1 | import { BaseSpectatorOptions, getDefaultBaseOptions } from '../base/options'; 2 | import { merge } from '../internals/merge'; 3 | import { AtLeastOneRequired, OptionalsRequired } from '../types'; 4 | 5 | export type SpectatorInjectionContextOptions = AtLeastOneRequired< 6 | Pick 7 | >; 8 | 9 | const defaultFunctionOptions: OptionalsRequired = { 10 | ...getDefaultBaseOptions(), 11 | }; 12 | 13 | /** 14 | * @internal 15 | */ 16 | export type FullInjectionContextOptions = Required & Required; 17 | 18 | /** 19 | * @internal 20 | */ 21 | export function getDefaultFunctionOptions(overrides: SpectatorInjectionContextOptions): FullInjectionContextOptions { 22 | return merge(defaultFunctionOptions, overrides) as FullInjectionContextOptions; 23 | } 24 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-injection-context/spectator-injection-context.ts: -------------------------------------------------------------------------------- 1 | import { BaseSpectator } from '../base/base-spectator'; 2 | 3 | /** 4 | * @publicApi 5 | */ 6 | export class SpectatorInjectionContext extends BaseSpectator { 7 | constructor() { 8 | super(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-pipe/initial-module.ts: -------------------------------------------------------------------------------- 1 | import { initialModule, ModuleMetadata } from '../base/initial-module'; 2 | import { declareInModule } from '../utils'; 3 | 4 | import { SpectatorPipeOptions } from './options'; 5 | 6 | /** 7 | * @internal 8 | */ 9 | export function initialSpectatorPipeModule(options: Required>): ModuleMetadata { 10 | const moduleMetadata = initialModule(options); 11 | 12 | declareInModule(moduleMetadata, options.pipe); 13 | moduleMetadata.declarations.push(options.host); 14 | 15 | return moduleMetadata; 16 | } 17 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-pipe/options.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { merge } from '../internals/merge'; 4 | import { OptionalsRequired } from '../types'; 5 | import { BaseSpectatorOptions, getDefaultBaseOptions } from '../base/options'; 6 | import { HostComponent } from '../spectator-host/host-component'; 7 | 8 | /** 9 | * @publicApi 10 | */ 11 | export interface SpectatorPipeOptions extends BaseSpectatorOptions { 12 | pipe: Type

; 13 | host?: Type; 14 | detectChanges?: boolean; 15 | template?: string; 16 | } 17 | 18 | const defaultSpectatorPipeOptions: OptionalsRequired> = { 19 | ...getDefaultBaseOptions(), 20 | host: HostComponent, 21 | detectChanges: true, 22 | template: '', 23 | }; 24 | 25 | /** 26 | * @internal 27 | */ 28 | export function getSpectatorPipeDefaultOptions(overrides?: SpectatorPipeOptions): Required> { 29 | return merge(defaultSpectatorPipeOptions, overrides); 30 | } 31 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-pipe/spectator-pipe.ts: -------------------------------------------------------------------------------- 1 | import { DebugElement } from '@angular/core'; 2 | import { ComponentFixture } from '@angular/core/testing'; 3 | 4 | import { BaseSpectator } from '../base/base-spectator'; 5 | import { setHostProps } from '../internals/query'; 6 | import { HostComponent } from '../spectator-host/host-component'; 7 | 8 | /** 9 | * @publicApi 10 | */ 11 | export class SpectatorPipe extends BaseSpectator { 12 | constructor( 13 | public hostComponent: H, 14 | public fixture: ComponentFixture, 15 | public debugElement: DebugElement, 16 | public element: Element, 17 | ) { 18 | super(); 19 | } 20 | 21 | public detectChanges(): void { 22 | this.fixture.detectChanges(); 23 | } 24 | 25 | public setHostInput(input: H extends HostComponent ? any : Partial): void; 26 | public setHostInput(input: H extends HostComponent ? any : K, inputValue: H extends HostComponent ? any : H[K]): void; 27 | public setHostInput(input: any, value?: any): void { 28 | setHostProps(this.fixture.componentRef, input, value); 29 | this.detectChanges(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-routing/options.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { merge } from '../internals/merge'; 4 | import { getSpectatorDefaultOptions, SpectatorOptions } from '../spectator/options'; 5 | import { OptionalsRequired } from '../types'; 6 | 7 | import { RouteOptions } from './route-options'; 8 | 9 | export type SpectatorRoutingOptions = SpectatorOptions & 10 | RouteOptions & { 11 | stubsEnabled?: boolean; 12 | routes?: Routes; 13 | }; 14 | 15 | const defaultRoutingOptions: OptionalsRequired> = { 16 | ...getSpectatorDefaultOptions(), 17 | params: {}, 18 | queryParams: {}, 19 | data: {}, 20 | fragment: null, 21 | stubsEnabled: true, 22 | routes: [], 23 | url: [], 24 | root: null, 25 | parent: null, 26 | children: null, 27 | firstChild: null, 28 | }; 29 | 30 | /** 31 | * @internal 32 | */ 33 | export function getRoutingDefaultOptions(overrides: SpectatorRoutingOptions): Required> { 34 | return merge(defaultRoutingOptions, overrides); 35 | } 36 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-routing/route-options.ts: -------------------------------------------------------------------------------- 1 | import { Data, Params, UrlSegment } from '@angular/router'; 2 | import { ActivatedRouteStub } from './activated-route-stub'; 3 | 4 | export interface RouteOptions { 5 | params?: Params; 6 | queryParams?: Params; 7 | data?: Data; 8 | fragment?: string | null; 9 | url?: UrlSegment[]; 10 | root?: ActivatedRouteStub | null; 11 | parent?: ActivatedRouteStub | null; 12 | firstChild?: ActivatedRouteStub | null; 13 | children?: ActivatedRouteStub[] | null; 14 | } 15 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-routing/router-stub.ts: -------------------------------------------------------------------------------- 1 | import { Router, Event } from '@angular/router'; 2 | 3 | export abstract class RouterStub extends Router { 4 | public abstract emitRouterEvent(event: Event): void; 5 | public abstract serializeUrl(): string; 6 | } 7 | 8 | export function isRouterStub(router: Router): router is RouterStub { 9 | return 'emitRouterEvent' in router; 10 | } 11 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-service/initial-module.ts: -------------------------------------------------------------------------------- 1 | import { initialModule, ModuleMetadata } from '../base/initial-module'; 2 | 3 | import { SpectatorServiceOptions } from './options'; 4 | 5 | /** 6 | * @internal 7 | */ 8 | export function initialServiceModule(options: Required>): ModuleMetadata { 9 | const moduleMetadata = initialModule(options); 10 | 11 | moduleMetadata.providers.push(options.service); 12 | 13 | return moduleMetadata; 14 | } 15 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-service/options.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { getDefaultBaseOptions, BaseSpectatorOptions } from '../base/options'; 4 | import { merge } from '../internals/merge'; 5 | import { OptionalsRequired } from '../types'; 6 | 7 | export interface SpectatorServiceOptions extends BaseSpectatorOptions { 8 | service: Type; 9 | } 10 | 11 | const defaultServiceOptions: OptionalsRequired = { 12 | ...getDefaultBaseOptions(), 13 | }; 14 | 15 | /** 16 | * @internal 17 | */ 18 | export function getDefaultServiceOptions(overrides: SpectatorServiceOptions): Required> { 19 | return merge(defaultServiceOptions, overrides) as Required>; 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator-service/spectator-service.ts: -------------------------------------------------------------------------------- 1 | import { BaseSpectator } from '../base/base-spectator'; 2 | 3 | /** 4 | * @publicApi 5 | */ 6 | export class SpectatorService extends BaseSpectator { 7 | constructor(public service: S) { 8 | super(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator/initial-module.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 2 | 3 | import { initialModule, ModuleMetadata } from '../base/initial-module'; 4 | import { declareInModule } from '../utils'; 5 | 6 | import { SpectatorOptions } from './options'; 7 | 8 | /** 9 | * @internal 10 | */ 11 | export function initialSpectatorModule(options: Required>): ModuleMetadata { 12 | const moduleMetadata = initialModule(options); 13 | 14 | if (options.declareComponent) { 15 | declareInModule(moduleMetadata, options.component); 16 | } 17 | 18 | moduleMetadata.schemas = [options.shallow ? NO_ERRORS_SCHEMA : options.schemas || []]; 19 | 20 | return moduleMetadata; 21 | } 22 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/spectator/options.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { getDefaultBaseOptions, BaseSpectatorOptions } from '../base/options'; 4 | import { merge } from '../internals/merge'; 5 | import { OptionalsRequired } from '../types'; 6 | 7 | /** 8 | * @publicApi 9 | */ 10 | export interface SpectatorOptions extends BaseSpectatorOptions { 11 | component: Type; 12 | shallow?: boolean; 13 | componentProviders?: any[]; 14 | componentViewProviders?: any[]; 15 | componentImports?: any[]; 16 | detectChanges?: boolean; 17 | declareComponent?: boolean; 18 | componentMocks?: Type[]; 19 | componentViewProvidersMocks?: Type[]; 20 | } 21 | 22 | const defaultSpectatorOptions: OptionalsRequired> = { 23 | ...getDefaultBaseOptions(), 24 | shallow: false, 25 | declareComponent: true, 26 | detectChanges: true, 27 | componentProviders: [], 28 | componentViewProviders: [], 29 | componentImports: [], 30 | componentMocks: [], 31 | componentViewProvidersMocks: [], 32 | }; 33 | 34 | /** 35 | * @internal 36 | */ 37 | export function getSpectatorDefaultOptions(overrides?: SpectatorOptions): Required> { 38 | return merge(defaultSpectatorOptions, overrides); 39 | } 40 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken, AbstractType, Type } from '@angular/core'; 2 | 3 | /** Type representing valid typesafe token types for provider binding. */ 4 | export type Token = Type | InjectionToken | AbstractType; 5 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/type-in-element.ts: -------------------------------------------------------------------------------- 1 | import { dispatchFakeEvent } from './dispatch-events'; 2 | 3 | /** 4 | * Focuses an input or textarea, sets its value and dispatches 5 | * the `input` or `textarea` event, simulating the user typing. 6 | * @param value Value to be set on the input. 7 | * @param element Element onto which to set the value. 8 | * 9 | * typeInElement('al', input); 10 | */ 11 | export function typeInElement(value: string, element: HTMLElement | Document | Window): void { 12 | if (!(element instanceof HTMLInputElement) && !(element instanceof HTMLTextAreaElement)) { 13 | return; 14 | } 15 | 16 | element.focus(); 17 | element.value = value; 18 | dispatchFakeEvent(element, 'input', true); 19 | } 20 | -------------------------------------------------------------------------------- /projects/spectator/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { Type, isStandalone } from '@angular/core'; 2 | import { ModuleMetadata } from './base/initial-module'; 3 | 4 | export function isRunningInJsDom() { 5 | return navigator.userAgent.includes('Node.js') || navigator.userAgent.includes('jsdom'); 6 | } 7 | 8 | export function coerceArray(value: T | T[]): T[]; 9 | export function coerceArray(value: T | readonly T[]): readonly T[]; 10 | export function coerceArray(value: T | T[]): T[] { 11 | return Array.isArray(value) ? value : [value]; 12 | } 13 | 14 | export function declareInModule(moduleMetadata: ModuleMetadata, type: Type) { 15 | if (isStandalone(type)) { 16 | moduleMetadata.imports.push(type); 17 | } else { 18 | moduleMetadata.declarations.push(type); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/test/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | const routes: Routes = []; 5 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule], 9 | }) 10 | export class AppRoutingModule {} 11 | -------------------------------------------------------------------------------- /projects/spectator/test/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /projects/spectator/test/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/projects/spectator/test/app.component.scss -------------------------------------------------------------------------------- /projects/spectator/test/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'], 7 | }) 8 | export class AppComponent { 9 | title = 'app'; 10 | } 11 | -------------------------------------------------------------------------------- /projects/spectator/test/async-input/async-input.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync } from '@angular/core/testing'; 2 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator'; 3 | 4 | import { AsyncInputComponent } from './async-input.component'; 5 | 6 | describe('ZippyComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory(AsyncInputComponent); 10 | 11 | it('should work', () => { 12 | const { component } = createHost(``); 13 | expect(component).toBeDefined(); 14 | }); 15 | 16 | it('should not be visible', () => { 17 | host = createHost(``); 18 | host.setHostInput('widgets', ''); 19 | expect(host.query('div')).not.toExist(); 20 | }); 21 | 22 | it('should be visible', fakeAsync(() => { 23 | host = createHost(``, { 24 | detectChanges: true, 25 | hostProps: { 26 | widgets: '', 27 | }, 28 | }); 29 | host.tick(); 30 | host.detectChanges(); 31 | expect(host.query('div')).toExist(); 32 | })); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/test/async-input/async-input.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-async-input', 5 | template: `

Hello
`, 6 | standalone: false, 7 | }) 8 | export class AsyncInputComponent { 9 | public show; 10 | 11 | @Input() public set widgets(v: any) { 12 | Promise.resolve().then(() => { 13 | this.show = true; 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /projects/spectator/test/async/async.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { createComponentFactory, Spectator } from '@ngneat/spectator'; 3 | 4 | @Component({ 5 | selector: 'app-foo', 6 | template: '', 7 | host: { 8 | '[class.bar]': 'bar', 9 | }, 10 | standalone: false, 11 | }) 12 | class FooComponent { 13 | @Input() bar!: boolean; 14 | } 15 | 16 | describe('FooComponenent', () => { 17 | const createComponent = createComponentFactory({ 18 | component: FooComponent, 19 | }); 20 | 21 | let spectator: Spectator; 22 | 23 | beforeEach(() => { 24 | spectator = createComponent(); 25 | }); 26 | 27 | it('should set the class name "bar"', () => { 28 | spectator.setInput({ bar: true }); 29 | 30 | expect(spectator.element).toHaveClass('bar'); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /projects/spectator/test/async/async.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; 2 | 3 | import { QueryService } from '../query.service'; 4 | 5 | @Component({ 6 | selector: 'app-async', 7 | template: `

async works!

`, 8 | changeDetection: ChangeDetectionStrategy.OnPush, 9 | styles: [], 10 | standalone: false, 11 | }) 12 | export class AsyncComponent implements OnInit { 13 | public show$; 14 | constructor(public query: QueryService) {} 15 | 16 | public ngOnInit(): void { 17 | this.show$ = this.query.select(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /projects/spectator/test/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; 2 | 3 | import { AuthService } from './auth.service'; 4 | import { DateService } from './date.service'; 5 | import { DynamicComponent } from './dynamic/dynamic.component'; 6 | 7 | describe('AuthService', () => { 8 | let spectator: SpectatorService; 9 | const createService = createServiceFactory({ 10 | service: AuthService, 11 | entryComponents: [DynamicComponent], 12 | mocks: [DateService], 13 | }); 14 | 15 | beforeEach(() => (spectator = createService())); 16 | 17 | it('should not be logged in', () => { 18 | const dateService = spectator.inject(DateService); 19 | dateService.isExpired.and.returnValue(true); 20 | expect(spectator.service.isLoggedIn()).toBeFalsy(); 21 | }); 22 | 23 | it('should be logged in', () => { 24 | const dateService = spectator.inject(DateService); 25 | dateService.isExpired.and.returnValue(false); 26 | expect(spectator.service.isLoggedIn()).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /projects/spectator/test/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { DateService } from './date.service'; 4 | 5 | @Injectable() 6 | export class AuthService { 7 | constructor(private readonly dateService: DateService) {} 8 | 9 | public isLoggedIn(): boolean { 10 | return !this.dateService.isExpired('timestamp'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/spectator/test/auto-focus/auto-focus.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[datoAutoFocus]', 5 | standalone: false, 6 | }) 7 | export class AutoFocusDirective { 8 | @Input() public set datoAutoFocus(value: boolean) { 9 | if (value) { 10 | this.host.nativeElement.focus(); 11 | } 12 | } 13 | 14 | public constructor(private readonly host: ElementRef) {} 15 | 16 | public method(): void { 17 | // eslint-disable-next-line no-console 18 | console.log(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/test/auto-focus/auto-focus.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator'; 2 | 3 | import { AutoFocusDirective } from './auto-focus.directive'; 4 | import { AutoFocusModule } from './auto-focus.module'; 5 | 6 | describe('AutoFocusDirectiveModule', () => { 7 | let spectator: SpectatorDirective; 8 | 9 | const createDirective = createDirectiveFactory({ 10 | directive: AutoFocusDirective, 11 | imports: [AutoFocusModule], 12 | declareDirective: false, 13 | }); 14 | 15 | it('should be declare AutoFocusDirective', () => { 16 | spectator = createDirective(``); 17 | expect(spectator.directive).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /projects/spectator/test/auto-focus/auto-focus.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AutoFocusDirective } from './auto-focus.directive'; 5 | 6 | @NgModule({ 7 | imports: [CommonModule], 8 | declarations: [AutoFocusDirective], 9 | exports: [AutoFocusDirective], 10 | }) 11 | export class AutoFocusModule {} 12 | -------------------------------------------------------------------------------- /projects/spectator/test/button/button.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, Output } from '@angular/core'; 2 | 3 | import { QueryService } from '../query.service'; 4 | 5 | /* eslint-disable @angular-eslint/template/no-call-expression */ 6 | 7 | @Component({ 8 | selector: 'app-button', 9 | template: ` 10 | 11 |

{{ queryService.selectName() | async }}

12 |
Context menu
13 |
dblclick
14 | `, 15 | providers: [QueryService], 16 | styles: [], 17 | standalone: false, 18 | }) 19 | export class ButtonComponent { 20 | @Input() public className = 'success'; 21 | @Input() public title = ''; 22 | 23 | // eslint-disable-next-line @angular-eslint/no-output-native 24 | @Output() public readonly click = new EventEmitter(); 25 | 26 | constructor(public queryService: QueryService) {} 27 | 28 | public onClick($event: any): void { 29 | this.click.emit($event); 30 | } 31 | 32 | public contextmenu() {} 33 | 34 | public dblclick() {} 35 | } 36 | -------------------------------------------------------------------------------- /projects/spectator/test/calc-textarea/calc-textarea.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator'; 2 | 3 | import { CalcTextAreaComponent } from './calc-textarea.component'; 4 | 5 | describe('CalcTextAreaComponent', () => { 6 | let spectator: Spectator; 7 | const createComponent = createComponentFactory(CalcTextAreaComponent); 8 | 9 | it('should be defined', () => { 10 | spectator = createComponent(); 11 | expect(spectator.component).toBeTruthy(); 12 | }); 13 | 14 | it('should calc the value', () => { 15 | spectator = createComponent(); 16 | const a = spectator.query('.a') as HTMLTextAreaElement; 17 | const b = spectator.query('.b') as HTMLTextAreaElement; 18 | spectator.typeInElement('1', a); 19 | spectator.typeInElement('2', b); 20 | 21 | expect(spectator.query('.result')).toHaveText('12'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /projects/spectator/test/calc-textarea/calc-textarea.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-calc', 5 | template: ` 6 | 7 | 8 |

{{ a.value + b.value }}

9 | `, 10 | standalone: false, 11 | }) 12 | export class CalcTextAreaComponent {} 13 | -------------------------------------------------------------------------------- /projects/spectator/test/calc/calc.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { byLabel, createComponentFactory, Spectator } from '@ngneat/spectator'; 2 | import { CalcComponent } from './calc.component'; 3 | 4 | describe('CalcComponent', () => { 5 | let spectator: Spectator; 6 | const createComponent = createComponentFactory(CalcComponent); 7 | 8 | beforeEach(() => { 9 | spectator = createComponent(); 10 | }); 11 | 12 | it('should be defined', () => { 13 | expect(spectator.component).toBeTruthy(); 14 | }); 15 | 16 | it('should calc the value', () => { 17 | const a = spectator.query('.a') as HTMLInputElement; 18 | const b = spectator.query('.b') as HTMLInputElement; 19 | spectator.typeInElement('1', a); 20 | spectator.typeInElement('2', b); 21 | 22 | expect(spectator.query('.result')).toHaveText('12'); 23 | }); 24 | 25 | it('should calc the value by DOMSelector', () => { 26 | spectator.typeInElement('3', byLabel('a')); 27 | spectator.typeInElement('7', byLabel('b')); 28 | 29 | expect(spectator.query('.result')).toHaveText('37'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/spectator/test/calc/calc.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-calc', 5 | template: ` 6 | 10 | 14 |

{{ a.value + b.value }}

15 | `, 16 | standalone: false, 17 | }) 18 | export class CalcComponent {} 19 | -------------------------------------------------------------------------------- /projects/spectator/test/child-custom-event/child-custom-event-parent.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-child-custom-event-parent', 5 | template: ` 6 | 10 |

{{ eventValue }}

11 | `, 12 | standalone: false, 13 | }) 14 | export class ChildCustomEventParentComponent { 15 | public eventValue = ''; 16 | 17 | public onCustomEventUsingEventEmitter(eventValue: string): void { 18 | this.eventValue = eventValue; 19 | } 20 | 21 | public onCustomEventUsingOutputEmitter(eventValue: string): void { 22 | this.eventValue = eventValue; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /projects/spectator/test/child-custom-event/child-custom-event.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-child-custom-event', 5 | template: `

Custom child

`, 6 | standalone: false, 7 | }) 8 | export class ChildCustomEventComponent { 9 | @Output() customEventUsingEventEmitter = new EventEmitter(); 10 | customEventUsingOutputEmitter = output(); 11 | } 12 | -------------------------------------------------------------------------------- /projects/spectator/test/child-custom-event/child-custom-event.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ChildCustomEventParentComponent } from './child-custom-event-parent.component'; 5 | import { ChildCustomEventComponent } from './child-custom-event.component'; 6 | 7 | @NgModule({ 8 | imports: [CommonModule], 9 | declarations: [ChildCustomEventParentComponent, ChildCustomEventComponent], 10 | exports: [ChildCustomEventParentComponent], 11 | }) 12 | export class ChildCustomEventModule {} 13 | -------------------------------------------------------------------------------- /projects/spectator/test/child-service.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { inject, TestBed } from '@angular/core/testing'; 2 | 3 | import { ChildServiceService } from './child-service.service'; 4 | 5 | describe('ChildServiceService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [ChildServiceService], 9 | }); 10 | }); 11 | 12 | it('should be created', inject([ChildServiceService], (service: ChildServiceService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /projects/spectator/test/child-service.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class ChildServiceService {} 5 | -------------------------------------------------------------------------------- /projects/spectator/test/child/child.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { ChildServiceService } from '../child-service.service'; 4 | 5 | @Component({ 6 | selector: 'app-child', 7 | template: `

child works!

`, 8 | styles: [], 9 | standalone: false, 10 | }) 11 | export class ChildComponent { 12 | constructor(private readonly service: ChildServiceService) {} 13 | } 14 | -------------------------------------------------------------------------------- /projects/spectator/test/click/click.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync } from '@angular/core/testing'; 2 | import { byText, createComponentFactory, Spectator } from '@ngneat/spectator'; 3 | import { ClickComponent } from './click.component'; 4 | 5 | describe('ClickComponent', () => { 6 | let component: ClickComponent; 7 | let spectator: Spectator; 8 | 9 | const createComponent = createComponentFactory(ClickComponent); 10 | 11 | beforeEach(() => { 12 | spectator = createComponent(); 13 | component = spectator.component; 14 | }); 15 | 16 | it('should create', () => { 17 | expect(component).toBeTruthy(); 18 | }); 19 | 20 | it('should changed on click with click query shorthand', fakeAsync(() => { 21 | spectator.click('button'); 22 | spectator.tick(100); 23 | 24 | expect('p').toHaveText('changed'); 25 | })); 26 | 27 | it('should changed on click with click dom selector', fakeAsync(() => { 28 | spectator.click(byText('Change')); 29 | spectator.tick(100); 30 | 31 | expect('p').toHaveText('changed'); 32 | })); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/test/click/click.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { debounce } from 'helpful-decorators'; 3 | 4 | @Component({ 5 | selector: 'app-click', 6 | template: ` 7 | 8 |

{{ name }}

9 | `, 10 | styles: [], 11 | standalone: false, 12 | }) 13 | export class ClickComponent { 14 | public name = 'init'; 15 | 16 | @debounce(100) public onClick(): void { 17 | this.name = 'changed'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /projects/spectator/test/consum-dynamic/consume-dynamic.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator'; 2 | 3 | import { DynamicComponent } from '../dynamic/dynamic.component'; 4 | 5 | import { ConsumeDynamicComponent } from './consume-dynamic.component'; 6 | 7 | describe('ConsumeDynamicComponent', () => { 8 | let host: SpectatorHost; 9 | 10 | const createHost = createHostFactory({ 11 | declarations: [DynamicComponent], 12 | entryComponents: [DynamicComponent], 13 | component: ConsumeDynamicComponent, 14 | }); 15 | 16 | it('should work', () => { 17 | host = createHost(``); 18 | expect(host.component).toBeDefined(); 19 | }); 20 | 21 | it('should render the dynamic component', () => { 22 | host = createHost(``); 23 | expect(host.queryHost('.dynamic')).toHaveText('dynamic works!'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/test/consum-dynamic/consume-dynamic.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentFactoryResolver, OnInit, ViewContainerRef } from '@angular/core'; 2 | 3 | import { DynamicComponent } from '../dynamic/dynamic.component'; 4 | 5 | @Component({ 6 | selector: 'app-consume-dynamic', 7 | template: `

consume-dynamic works!

`, 8 | styles: [], 9 | standalone: false, 10 | }) 11 | export class ConsumeDynamicComponent implements OnInit { 12 | constructor( 13 | private readonly resolver: ComponentFactoryResolver, 14 | private readonly ref: ViewContainerRef, 15 | ) {} 16 | 17 | public ngOnInit(): void { 18 | const factory = this.resolver.resolveComponentFactory(DynamicComponent); 19 | this.ref.createComponent(factory); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /projects/spectator/test/consumer.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator'; 2 | import { Subject } from 'rxjs'; 3 | 4 | import { ConsumerService } from './consumer.service'; 5 | import { ProviderService } from './provider.service'; 6 | 7 | describe('ConsumerService', () => { 8 | const randomNumber = Math.random(); 9 | let spectator: SpectatorService; 10 | const createService = createServiceFactory({ 11 | service: ConsumerService, 12 | providers: [ 13 | mockProvider(ProviderService, { 14 | obs$: new Subject(), 15 | method: () => randomNumber, 16 | }), 17 | ], 18 | }); 19 | 20 | beforeEach(() => (spectator = createService())); 21 | 22 | it('should consume mocked service with properties', () => { 23 | const provider = spectator.inject(ProviderService); 24 | expect(spectator.service.lastValue).toBeUndefined(); 25 | provider.obs$.next('hey you'); 26 | expect(spectator.service.lastValue).toBe('hey you'); 27 | }); 28 | 29 | it('should consume mocked service methods', () => { 30 | expect(spectator.service.consumeProvider()).toBe(randomNumber); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /projects/spectator/test/consumer.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { ProviderService } from './provider.service'; 4 | 5 | @Injectable({ 6 | providedIn: 'root', 7 | }) 8 | export class ConsumerService { 9 | public lastValue: any; 10 | 11 | constructor(private readonly provider: ProviderService) { 12 | provider.obs$.subscribe((val) => this.update(val)); 13 | } 14 | 15 | public update(val: string): void { 16 | this.lastValue = val; 17 | } 18 | 19 | public consumeProvider(): number { 20 | return this.provider.method(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/test/date.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { inject, TestBed } from '@angular/core/testing'; 2 | 3 | import { DateService } from './date.service'; 4 | 5 | describe('DateService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [DateService], 9 | }); 10 | }); 11 | 12 | it('should be created', inject([DateService], (service: DateService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /projects/spectator/test/date.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class DateService { 5 | public isExpired(time: any): boolean { 6 | // ... 7 | return false; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /projects/spectator/test/directive-providers.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { FormBuilder } from '@angular/forms'; 2 | import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator'; 3 | 4 | import { DirectiveProviderDirective, directiveProviderToken } from './directive-providers.directive'; 5 | 6 | describe('DirectiveProviderDirective', () => { 7 | let host: SpectatorDirective; 8 | 9 | const createHost = createDirectiveFactory({ 10 | directive: DirectiveProviderDirective, 11 | directiveProviders: [{ provide: directiveProviderToken, useValue: 'notTest' }], 12 | directiveMocks: [FormBuilder], 13 | template: `
Testing Directive Providers
`, 14 | }); 15 | 16 | it('should inject the provided value', () => { 17 | host = createHost(); 18 | expect(host.directive.provider).toEqual('notTest'); 19 | }); 20 | 21 | it('should use the default template by default', () => { 22 | host = createHost(); 23 | expect('.default').toExist(); 24 | }); 25 | 26 | it('should use more specific templates if given', () => { 27 | host = createHost(`
Testing Directive Providers
`); 28 | expect('.override').toExist(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /projects/spectator/test/directive-providers.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Inject, InjectionToken } from '@angular/core'; 2 | import { FormBuilder } from '@angular/forms'; 3 | 4 | export const directiveProviderToken = new InjectionToken('DirectiveProviderToken'); 5 | 6 | @Directive({ 7 | selector: '[directiveProvider]', 8 | providers: [{ provide: directiveProviderToken, useValue: 'test' }], 9 | standalone: false, 10 | }) 11 | export class DirectiveProviderDirective { 12 | constructor( 13 | @Inject(directiveProviderToken) public provider: string, 14 | private fb: FormBuilder, 15 | ) {} 16 | } 17 | -------------------------------------------------------------------------------- /projects/spectator/test/download/download.component.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { Component, Output, EventEmitter } from '@angular/core'; 3 | 4 | @Component({ 5 | selector: 'lib-download', 6 | template: ` 7 |

Download comp

8 | > 9 | `, 10 | standalone: false, 11 | }) 12 | export class DownloadComponent { 13 | @Output() selectedFile = new EventEmitter(); 14 | 15 | onDownloadClick() { 16 | this.selectedFile.emit('someValue'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /projects/spectator/test/dropzone/dropzone.component.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { Component, EventEmitter, Input, Output } from '@angular/core'; 3 | 4 | @Component({ 5 | selector: 'lib-dropzone', 6 | template: ` `, 7 | standalone: false, 8 | }) 9 | export class DropzoneComponent { 10 | @Input() allowCSV; 11 | @Output() template = new EventEmitter(); 12 | 13 | onTemplateSelectedForDownload(file: string) { 14 | this.template.emit(file); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /projects/spectator/test/dynamic/dynamic.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-dynamic', 5 | template: `

dynamic works!

`, 6 | styles: [], 7 | standalone: false, 8 | }) 9 | export class DynamicComponent {} 10 | -------------------------------------------------------------------------------- /projects/spectator/test/error-unknown/error-unknown-element.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ErrorUnknownElementComponent } from './error-unknown-element.component'; 5 | 6 | describe('ErrorUnknownElementComponent', () => { 7 | const createComponent = createComponentFactory({ 8 | component: ErrorUnknownElementComponent, 9 | imports: [CommonModule], 10 | errorOnUnknownElements: true, 11 | }); 12 | 13 | it('should throw an error when creating the component', () => { 14 | expect(() => createComponent()).toThrowError(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/spectator/test/error-unknown/error-unknown-element.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | /* eslint-disable @angular-eslint/template/no-call-expression */ 4 | 5 | @Component({ 6 | selector: 'app-use-unknown-element', 7 | template: ``, 8 | standalone: false, 9 | }) 10 | export class ErrorUnknownElementComponent {} 11 | -------------------------------------------------------------------------------- /projects/spectator/test/error-unknown/error-unknown-property.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ErrorUnknownPropertyComponent } from './error-unknown-property.component'; 5 | 6 | describe('ErrorUnknownPropertyComponent', () => { 7 | const createComponent = createComponentFactory({ 8 | component: ErrorUnknownPropertyComponent, 9 | imports: [CommonModule], 10 | errorOnUnknownProperties: true, 11 | }); 12 | 13 | it('should throw an error when creating the component', () => { 14 | expect(() => createComponent()).toThrowError(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/spectator/test/error-unknown/error-unknown-property.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | /* eslint-disable @angular-eslint/template/no-call-expression */ 4 | 5 | @Component({ 6 | selector: 'app-use-unknown-property', 7 | template: ``, 8 | standalone: false, 9 | }) 10 | export class ErrorUnknownPropertyComponent {} 11 | -------------------------------------------------------------------------------- /projects/spectator/test/events/events.component.html: -------------------------------------------------------------------------------- 1 |

{{ event }}

2 | 11 | -------------------------------------------------------------------------------- /projects/spectator/test/events/events.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/projects/spectator/test/events/events.component.scss -------------------------------------------------------------------------------- /projects/spectator/test/events/events.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-events', 5 | templateUrl: './events.component.html', 6 | styleUrls: ['./events.component.scss'], 7 | standalone: false, 8 | }) 9 | export class EventsComponent { 10 | public event = ''; 11 | 12 | public onFocus(): void { 13 | this.event = 'focus'; 14 | } 15 | 16 | public onBlur(): void { 17 | this.event = 'blur'; 18 | } 19 | 20 | public onPressA(): void { 21 | this.event = 'pressed a'; 22 | } 23 | 24 | public onPressCtrlA(): void { 25 | this.event = 'pressed ctrl.a'; 26 | } 27 | 28 | public onPressCtrlShiftA(): void { 29 | this.event = 'pressed ctrl.shift.a'; 30 | } 31 | 32 | public onPressDot(): void { 33 | this.event = 'pressed dot'; 34 | } 35 | 36 | public onPressArrowLeft(event: KeyboardEvent): void { 37 | this.event = `pressed ${event.key}:${event.keyCode}`; 38 | } 39 | 40 | public onPressArrowRight(event: KeyboardEvent): void { 41 | this.event = `pressed ArrowRight:${event.keyCode}`; 42 | } 43 | 44 | public onPressArrowUp(event: KeyboardEvent): void { 45 | this.event = `pressed ${event.key}:${event.code}`; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /projects/spectator/test/fg/fg.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { SpectatorHost, createHostFactory } from '@ngneat/spectator'; 2 | import { Component } from '@angular/core'; 3 | import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; 4 | 5 | import { FgComponent } from './fg.component'; 6 | 7 | @Component({ 8 | selector: 'app-custom-host', 9 | template: '', 10 | standalone: false, 11 | }) 12 | class CustomHostComponent { 13 | public group = new FormGroup({ 14 | name: new FormControl('name'), 15 | }); 16 | } 17 | 18 | describe('With Custom Host Component', () => { 19 | let host: SpectatorHost; 20 | 21 | const createHost = createHostFactory({ 22 | component: FgComponent, 23 | imports: [ReactiveFormsModule], 24 | host: CustomHostComponent, 25 | }); 26 | 27 | it('should display the host component title', () => { 28 | host = createHost(``); 29 | expect(host.component).toBeDefined(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/spectator/test/fg/fg.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormGroup } from '@angular/forms'; 3 | 4 | @Component({ 5 | selector: 'app-fg', 6 | template: ` 7 |
8 | 9 |
10 | `, 11 | styles: [], 12 | standalone: false, 13 | }) 14 | export class FgComponent { 15 | @Input() public group?: FormGroup; 16 | } 17 | -------------------------------------------------------------------------------- /projects/spectator/test/focus/test-focus.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-test-focus', 5 | template: ` 6 | `, 7 | changeDetection: ChangeDetectionStrategy.OnPush, 8 | host: { 9 | '[attr.tabindex]': '0', 10 | '(focus)': 'countFocus("app-test-focus")', 11 | '(blur)': 'countBlur("app-test-focus")', 12 | }, 13 | standalone: false, 14 | }) 15 | export class TestFocusComponent { 16 | private readonly focusCounts = new Map(); 17 | private readonly blurCounts = new Map(); 18 | 19 | public countFocus(id: string) { 20 | this.focusCounts.set(id, this.focusCount(id) + 1); 21 | } 22 | 23 | public countBlur(id: string) { 24 | this.blurCounts.set(id, this.blurCount(id) + 1); 25 | } 26 | 27 | public focusCount(id: string): number { 28 | return this.focusCounts.get(id) ?? 0; 29 | } 30 | 31 | public blurCount(id: string): number { 32 | return this.blurCounts.get(id) ?? 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /projects/spectator/test/form-input/form-input.component.html: -------------------------------------------------------------------------------- 1 |

2 | form-input works! 3 |

4 | -------------------------------------------------------------------------------- /projects/spectator/test/form-input/form-input.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/projects/spectator/test/form-input/form-input.component.scss -------------------------------------------------------------------------------- /projects/spectator/test/form-input/form-input.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; 2 | import { FormGroup } from '@angular/forms'; 3 | 4 | /** 5 | * a Subnet is a form of writing an IP Range (also known as CIDR) 6 | */ 7 | @Component({ 8 | selector: 'app-form-input', 9 | template: ` 10 |
11 | 12 |
13 |

14 | `, 15 | changeDetection: ChangeDetectionStrategy.OnPush, 16 | standalone: false, 17 | }) 18 | export class FormInputComponent { 19 | @Input() public subnetControl?: FormGroup; 20 | @Input() public enableSubnet?: boolean; 21 | 22 | @Output() public readonly deleteSubnet = new EventEmitter(); 23 | 24 | public emitSubnetDelete(): void { 25 | this.deleteSubnet.emit(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/spectator/test/function-output/function-output.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, input, Output, output, ɵINPUT_SIGNAL_BRAND_WRITE_TYPE } from '@angular/core'; 2 | import { ReplaySubject } from 'rxjs'; 3 | 4 | @Component({ 5 | selector: 'app-function-output', 6 | template: ` `, 7 | standalone: true, 8 | }) 9 | export class FunctionOutputComponent { 10 | public buttonClick = output(); 11 | 12 | @Output() 13 | public buttonClickedEvent = new EventEmitter(); 14 | 15 | @Output() 16 | public buttonClickedSubject = new ReplaySubject(); 17 | 18 | protected buttonClicked(): void { 19 | this.buttonClick.emit(true); 20 | this.buttonClickedEvent.emit(true); 21 | this.buttonClickedSubject.next(true); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /projects/spectator/test/hello/hello.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/projects/spectator/test/hello/hello.component.css -------------------------------------------------------------------------------- /projects/spectator/test/hello/hello.component.html: -------------------------------------------------------------------------------- 1 |

2 | hello works! 3 |

4 | -------------------------------------------------------------------------------- /projects/spectator/test/hello/hello.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; 2 | 3 | import { TranslateService } from '../translate.service'; 4 | 5 | @Component({ 6 | selector: 'hello', 7 | template: ` 8 |
9 |

{{ title | translate }}

10 |

11 | {{ title | translate }} 12 |

13 |
14 | 15 |
widthRaw is not set
16 |
Width is: {{ width }}
17 | `, 18 | standalone: false, 19 | }) 20 | export class HelloComponent implements OnChanges { 21 | public get width(): string | number | undefined { 22 | return typeof this.widthRaw === 'number' ? `${this.widthRaw}px` : this.widthRaw; 23 | } 24 | 25 | @Input() public title?: string; 26 | @Input() public widthRaw?: string | number; 27 | 28 | constructor(private readonly translate: TranslateService) {} 29 | 30 | public ngOnChanges(s: SimpleChanges): void {} 31 | } 32 | -------------------------------------------------------------------------------- /projects/spectator/test/highlight.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator'; 2 | 3 | import { HighlightDirective } from './highlight.directive'; 4 | 5 | describe('HighlightDirective', () => { 6 | let host: SpectatorHost; 7 | 8 | const createHost = createHostFactory(HighlightDirective); 9 | 10 | it('should change the background color', () => { 11 | host = createHost(`
Testing HighlightDirective
`); 12 | 13 | host.dispatchMouseEvent(host.element, 'mouseover'); 14 | 15 | expect(host.element).toHaveStyle({ 16 | backgroundColor: 'rgba(0,0,0, 0.1)', 17 | }); 18 | 19 | host.dispatchMouseEvent(host.element, 'mouseout'); 20 | expect(host.element).toHaveStyle({ 21 | backgroundColor: '#fff', 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /projects/spectator/test/highlight.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostBinding, HostListener } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[highlight]', 5 | standalone: false, 6 | }) 7 | export class HighlightDirective { 8 | @HostBinding('style.background-color') public backgroundColor?: string; 9 | 10 | @HostListener('mouseover') public onHover(): void { 11 | this.backgroundColor = 'rgba(0,0,0, 0.1)'; 12 | } 13 | 14 | @HostListener('mouseout') public onLeave(): void { 15 | this.backgroundColor = '#fff'; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/spectator/test/integration/integration-child.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-integration-child', 5 | template: `

integration-child works!

`, 6 | styles: [], 7 | standalone: false, 8 | }) 9 | export class IntegrationChildComponent {} 10 | -------------------------------------------------------------------------------- /projects/spectator/test/integration/integration-parent.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { Spectator, createComponentFactory } from '@ngneat/spectator'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | 4 | import { IntegrationParentComponent } from './integration-parent.component'; 5 | import { IntegrationModule } from './integration.module'; 6 | 7 | describe('IntegrationParentComponent', () => { 8 | let spectator: Spectator; 9 | const createComponent = createComponentFactory({ 10 | component: IntegrationParentComponent, 11 | imports: [IntegrationModule, HttpClientTestingModule], 12 | declareComponent: false, 13 | }); 14 | 15 | it('should exist', () => { 16 | spectator = createComponent(); 17 | expect(spectator.component).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /projects/spectator/test/integration/integration-parent.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { WidgetService } from '../widget.service'; 4 | 5 | @Component({ 6 | selector: 'app-integration-parent', 7 | template: ` `, 8 | styles: [], 9 | standalone: false, 10 | }) 11 | export class IntegrationParentComponent { 12 | constructor(public widgetService: WidgetService) {} 13 | } 14 | -------------------------------------------------------------------------------- /projects/spectator/test/integration/integration.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { WidgetService } from '../widget.service'; 5 | import { WidgetDataService } from '../widget-data.service'; 6 | 7 | import { IntegrationParentComponent } from './integration-parent.component'; 8 | import { IntegrationChildComponent } from './integration-child.component'; 9 | 10 | @NgModule({ 11 | imports: [CommonModule], 12 | providers: [WidgetService, WidgetDataService], 13 | declarations: [IntegrationParentComponent, IntegrationChildComponent], 14 | exports: [IntegrationParentComponent], 15 | }) 16 | export class IntegrationModule {} 17 | -------------------------------------------------------------------------------- /projects/spectator/test/matchers/matcher-enhancements.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | export interface Dummy { 4 | label: string; 5 | active: boolean; 6 | } 7 | 8 | @Component({ 9 | selector: 'matcher-enhancements', 10 | template: ` 11 |
It should have
12 |
Some different text
13 |
And another one
14 |
15 | 16 | 17 | 18 | 19 |
20 | 21 | `, 22 | standalone: false, 23 | }) 24 | export class MatcherEnhancementsComponent { 25 | public dummyValue: Dummy = { label: 'this is a dummy value', active: true }; 26 | } 27 | -------------------------------------------------------------------------------- /projects/spectator/test/mocks.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockProvider } from '@ngneat/spectator'; 2 | 3 | import { WidgetService } from './widget.service'; 4 | 5 | describe('mockProvider', () => { 6 | it('should not modify the object passed in 2nd argument when running the mock factory', () => { 7 | const customPropertiesAndMethods: Partial> = { 8 | testingProperty: 'overriden', 9 | }; 10 | const { useFactory: factory } = mockProvider(WidgetService, customPropertiesAndMethods); 11 | factory(); 12 | expect(customPropertiesAndMethods).toEqual({ 13 | testingProperty: 'overriden', 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/spectator/test/ng-on-destroy.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, mockProvider } from '@ngneat/spectator'; 2 | import { Subject } from 'rxjs'; 3 | 4 | import { NgOnDestroyService, SubjectService } from './ng-on-destroy.service'; 5 | 6 | describe('NgOnDestroyService', () => { 7 | const subjectAcrossMultipleTests$ = new Subject(); 8 | 9 | const createService = createServiceFactory({ 10 | service: NgOnDestroyService, 11 | providers: [ 12 | mockProvider(SubjectService, { 13 | subject: subjectAcrossMultipleTests$, 14 | }), 15 | ], 16 | }); 17 | 18 | it('should subscribe to subject during', () => { 19 | const spectator = createService(); 20 | 21 | expect(spectator.service).toBeTruthy(); 22 | expect(subjectAcrossMultipleTests$.observers.length).toBe(1); 23 | }); 24 | 25 | it('should call spy only once on because previous were unsubscribe in ngOnDestroy', () => { 26 | const spectator = createService(); 27 | 28 | expect(spectator.service).toBeTruthy(); 29 | expect(subjectAcrossMultipleTests$.observers.length).toBe(1); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/spectator/test/ng-on-destroy.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnDestroy } from '@angular/core'; 2 | import { Subscription, Subject } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class SubjectService { 6 | public subject = new Subject(); 7 | } 8 | 9 | @Injectable({ providedIn: 'root' }) 10 | export class NgOnDestroyService implements OnDestroy { 11 | private readonly subscription: Subscription; 12 | 13 | constructor(private readonly subjectService: SubjectService) { 14 | this.subscription = this.subjectService.subject.subscribe(); 15 | } 16 | 17 | public ngOnDestroy(): void { 18 | this.subscription.unsubscribe(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/test/ngonchanges-input/ngonchanges-input.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator'; 2 | import { NgOnChangesInputComponent } from './ngonchanges-input.component'; 3 | 4 | describe('NgOnChangesInputComponent', () => { 5 | describe('with Spectator', () => { 6 | let spectator: Spectator; 7 | 8 | const createComponent = createComponentFactory({ 9 | component: NgOnChangesInputComponent, 10 | }); 11 | 12 | beforeEach(() => { 13 | spectator = createComponent(); 14 | }); 15 | 16 | it('should re-render when updating fields in ngOnChanges', () => { 17 | expect(spectator.query('button')).toBeDisabled(); 18 | expect(spectator.query('button')).toHaveText('Button disabled'); 19 | 20 | spectator.setInput({ btnDisabled: false }); 21 | expect(spectator.query('button')).not.toBeDisabled(); 22 | expect(spectator.query('button')).toHaveText('Button enabled'); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/test/ngonchanges-input/ngonchanges-input.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | standalone: true, 6 | template: ` 7 | 10 | `, 11 | changeDetection: ChangeDetectionStrategy.OnPush, 12 | }) 13 | export class NgOnChangesInputComponent { 14 | @Input() 15 | btnDisabled = true; 16 | 17 | btnText = 'Button disabled'; 18 | 19 | ngOnChanges() { 20 | this.btnText = this.btnDisabled ? 'Button disabled' : 'Button enabled'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/test/no-overwritten-providers/dummy.service.ts: -------------------------------------------------------------------------------- 1 | export class DummyService {} 2 | -------------------------------------------------------------------------------- /projects/spectator/test/no-overwritten-providers/no-overwritten-providers.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component } from '@angular/core'; 2 | 3 | import { DummyService } from './dummy.service'; 4 | 5 | @Component({ 6 | selector: 'app-component-without-overwritten-providers', 7 | template: ``, 8 | changeDetection: ChangeDetectionStrategy.OnPush, 9 | styles: [], 10 | providers: [ 11 | { 12 | provide: DummyService, 13 | useValue: new DummyService(), 14 | }, 15 | ], 16 | standalone: false, 17 | }) 18 | export class ComponentWithoutOverwrittenProvidersComponent { 19 | constructor(public dummy: DummyService) {} 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/test/overlay-custom-event/overlay-container.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-overlay-container', 5 | template: ` 6 | 7 |

{{ eventValue }}

8 | `, 9 | standalone: false, 10 | }) 11 | export class OverlayContainerComponent { 12 | public eventValue = ''; 13 | 14 | public onCustomEvent(eventValue: string): void { 15 | this.eventValue = eventValue; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/spectator/test/overlay-custom-event/overlay-container.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { OverlayModule } from '@angular/cdk/overlay'; 4 | 5 | import { OverlayContainerComponent } from './overlay-container.component'; 6 | import { OverlayContentComponent } from './overlay-content.component'; 7 | 8 | @NgModule({ 9 | imports: [CommonModule, OverlayModule], 10 | declarations: [OverlayContainerComponent, OverlayContentComponent], 11 | exports: [OverlayContainerComponent], 12 | }) 13 | export class OverlayContainerModule {} 14 | -------------------------------------------------------------------------------- /projects/spectator/test/overlay-custom-event/overlay-content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-overlay-content', 5 | template: `

Overlay Content

`, 6 | standalone: false, 7 | }) 8 | export class OverlayContentComponent { 9 | @Output() customEvent = new EventEmitter(); 10 | } 11 | -------------------------------------------------------------------------------- /projects/spectator/test/pipe/alternating-sum.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import { StatsService } from './stats.service'; 4 | 5 | @Pipe({ 6 | name: 'alternatingSum', 7 | standalone: false, 8 | }) 9 | export class AlternatingSumPipe implements PipeTransform { 10 | constructor(private readonly statsService: StatsService) {} 11 | 12 | public transform(value: number[]): number { 13 | return this.statsService.alternatingSum(value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /projects/spectator/test/pipe/average.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import { StatsService } from './stats.service'; 4 | 5 | @Pipe({ 6 | name: 'avg', 7 | standalone: false, 8 | }) 9 | export class AveragePipe implements PipeTransform { 10 | constructor(private readonly statsService: StatsService) {} 11 | 12 | public transform(value: number[]): number { 13 | return this.statsService.avg(value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /projects/spectator/test/pipe/stats.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root', 5 | }) 6 | export class StatsService { 7 | public alternatingSum(input: number[]): number { 8 | return input.reduce((sum, x, i) => sum + Math.pow(-1, i) * x, 0); 9 | } 10 | 11 | public sum(input: number[]): number { 12 | return input.reduce((sum, x) => sum + x, 0); 13 | } 14 | 15 | public avg(input: number[]): number { 16 | if (input.length === 0) { 17 | return 0; 18 | } 19 | 20 | return this.sum(input) / input.length; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/test/pipe/sum.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { SpectatorPipe, createPipeFactory } from '@ngneat/spectator'; 2 | 3 | import { StatsService } from './stats.service'; 4 | import { SumPipe } from './sum.pipe'; 5 | 6 | describe('SumPipe', () => { 7 | let spectator: SpectatorPipe; 8 | const createPipe = createPipeFactory(SumPipe); 9 | 10 | it('should sum up the given list of numbers (template)', () => { 11 | spectator = createPipe(`{{ [1, 2, 3] | sum }}`); 12 | expect(spectator.element).toHaveText('6'); 13 | }); 14 | 15 | it('should sum up the given list of numbers (prop)', () => { 16 | spectator = createPipe(`{{ prop | sum }}`, { 17 | hostProps: { 18 | prop: [1, 2, 3], 19 | }, 20 | }); 21 | expect(spectator.element).toHaveText('6'); 22 | }); 23 | 24 | it('should delegate the summation to the service', () => { 25 | const sum = () => 42; 26 | const provider = { provide: StatsService, useValue: { sum } }; 27 | spectator = createPipe(`{{ prop | sum }}`, { 28 | hostProps: { 29 | prop: [2, 40], 30 | }, 31 | providers: [provider], 32 | }); 33 | expect(spectator.element).toHaveText('42'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /projects/spectator/test/pipe/sum.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import { StatsService } from './stats.service'; 4 | 5 | @Pipe({ 6 | name: 'sum', 7 | standalone: false, 8 | }) 9 | export class SumPipe implements PipeTransform { 10 | constructor(private readonly statsService: StatsService) {} 11 | 12 | public transform(value: number[]): number { 13 | return this.statsService.sum(value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /projects/spectator/test/provider.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root', 6 | }) 7 | export class ProviderService { 8 | public obs$ = new Subject(); 9 | 10 | public method(): number { 11 | return 5; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /projects/spectator/test/query.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { of } from 'rxjs'; 3 | import { Observable } from 'rxjs/internal/Observable'; 4 | 5 | import { WidgetService } from './widget.service'; 6 | 7 | @Injectable() 8 | export abstract class AbstractQueryService { 9 | public abstract select(): Observable; 10 | public abstract selectName(): Observable; 11 | 12 | constructor(private readonly service: WidgetService) {} 13 | } 14 | 15 | @Injectable() 16 | export class QueryService extends AbstractQueryService { 17 | constructor(service: WidgetService) { 18 | super(service); 19 | } 20 | 21 | public selectName(): Observable { 22 | return of('Netanel'); 23 | } 24 | 25 | public select(): Observable { 26 | return of(false); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /projects/spectator/test/run-in-injection-context.spec.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable, InjectionToken, NgModule } from '@angular/core'; 2 | import { createInjectionContextFactory, SpectatorInjectionContext } from '@ngneat/spectator'; 3 | 4 | const TEST_TOKEN = new InjectionToken('simple-token'); 5 | 6 | @Injectable() 7 | export class TestService { 8 | flag = false; 9 | } 10 | 11 | @NgModule({ 12 | providers: [TestService], 13 | }) 14 | export class TestModule {} 15 | 16 | const testFn = (arg: any) => { 17 | const token = inject(TEST_TOKEN); 18 | const { flag } = inject(TestService); 19 | 20 | return { token, flag, arg }; 21 | }; 22 | 23 | describe('Run in injection context', () => { 24 | describe('with Spectator', () => { 25 | const createContext = createInjectionContextFactory({ imports: [TestModule], providers: [{ provide: TEST_TOKEN, useValue: 'abcd' }] }); 26 | 27 | let spectator: SpectatorInjectionContext; 28 | 29 | beforeEach(() => (spectator = createContext())); 30 | 31 | it('should execute fn in injection context', () => { 32 | const service = spectator.inject(TestService); 33 | service.flag = true; 34 | 35 | const result = spectator.runInInjectionContext(() => testFn(2)); 36 | expect(result).toEqual({ token: 'abcd', flag: true, arg: 2 }); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /projects/spectator/test/set-input/set-input.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | /* eslint-disable @angular-eslint/template/no-call-expression */ 4 | 5 | @Component({ 6 | selector: 'app-set-input', 7 | template: ``, 8 | standalone: false, 9 | }) 10 | export class SetInputComponent { 11 | public another; 12 | 13 | @Input() public one; 14 | @Input() public set two(value: any) { 15 | this.another = value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/spectator/test/signal-input/signal-input.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input, ɵINPUT_SIGNAL_BRAND_WRITE_TYPE } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-signal-input', 5 | template: ` 6 | @if (show()) { 7 |
Hello
8 | } 9 | `, 10 | standalone: true, 11 | }) 12 | export class SignalInputComponent { 13 | public show = input(false); 14 | } 15 | -------------------------------------------------------------------------------- /projects/spectator/test/simple-changes/simple-changes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnChanges, OnInit, SimpleChange, SimpleChanges } from '@angular/core'; 2 | 3 | /* eslint-disable @angular-eslint/template/no-call-expression */ 4 | 5 | @Component({ 6 | selector: 'app-simple-changes', 7 | template: ``, 8 | standalone: false, 9 | }) 10 | export class SimpleChangesComponent implements OnInit, OnChanges { 11 | @Input() public value; 12 | 13 | public hooks: string[] = []; 14 | public changes: SimpleChange[] = []; 15 | 16 | public ngOnInit(): void { 17 | this.hooks.push('ngOnInit'); 18 | } 19 | 20 | public ngOnChanges(changes: SimpleChanges): void { 21 | this.hooks.push('ngOnChanges'); 22 | 23 | if ('value' in changes) { 24 | this.changes.push(changes.value); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/spectator/test/spy-object/person.ts: -------------------------------------------------------------------------------- 1 | export class Person { 2 | public readonly name: string = 'Some name'; 3 | public birthYear = 1990; 4 | 5 | public sayHi(): string { 6 | return 'Hi!'; 7 | } 8 | 9 | public get age(): number { 10 | return 2019 - this.birthYear; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/spectator/test/spy-object/spy-object.spec.ts: -------------------------------------------------------------------------------- 1 | import { createSpyObject } from '@ngneat/spectator'; 2 | 3 | import { Person } from './person'; 4 | 5 | describe('SpyObject', () => { 6 | it('should mock all public methods', () => { 7 | const person = createSpyObject(Person); 8 | 9 | person.sayHi.andReturn('Bye!'); 10 | }); 11 | 12 | it('should enable spying on properties', () => { 13 | const person = createSpyObject(Person); 14 | person.birthYear = 1990; 15 | spyOnProperty(person, 'age', 'get').and.returnValue(29); 16 | 17 | expect(person.age).toBe(29); 18 | }); 19 | 20 | it('should enable setting properties by just assigning', () => { 21 | const person = createSpyObject(Person); 22 | person.birthYear = 1990; 23 | (person as any).age = 29; 24 | 25 | expect(person.age).toBe(29); 26 | }); 27 | 28 | it('should allow setting properties', () => { 29 | const person = createSpyObject(Person); 30 | 31 | person.birthYear = 1995; // should compile 32 | }); 33 | 34 | it('should allow setting readonly properties with cast method', () => { 35 | const person = createSpyObject(Person); 36 | 37 | person.castToWritable().name = 'Other name'; // should compile 38 | 39 | expect(person.name).toBe('Other name'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /projects/spectator/test/standalone/component/standalone-with-imports.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: `app-standalone-child`, 5 | template: `
This stands alone child!
`, 6 | standalone: true, 7 | }) 8 | export class StandaloneChildComponent {} 9 | 10 | @Component({ 11 | selector: `app-standalone-child`, 12 | template: `
Mocked!
`, 13 | standalone: true, 14 | }) 15 | export class MockStandaloneChildComponent {} 16 | 17 | @Component({ 18 | selector: `app-standalone-with-imports`, 19 | template: ``, 20 | standalone: true, 21 | imports: [StandaloneChildComponent], 22 | }) 23 | export class StandaloneWithImportsComponent {} 24 | -------------------------------------------------------------------------------- /projects/spectator/test/standalone/component/standalone.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, createHostFactory, Spectator, SpectatorHost } from '@ngneat/spectator'; 2 | import { StandaloneComponent } from './standalone.component'; 3 | 4 | describe('StandaloneComponent', () => { 5 | describe('with Spectator', () => { 6 | let spectator: Spectator; 7 | 8 | const createComponent = createComponentFactory({ 9 | component: StandaloneComponent, 10 | }); 11 | 12 | beforeEach(() => { 13 | spectator = createComponent(); 14 | }); 15 | 16 | it('should render a StandaloneComponent', () => { 17 | expect(spectator.query('#standalone')).toContainText('This stands alone!'); 18 | }); 19 | }); 20 | 21 | describe('with SpectatorHost', () => { 22 | let host: SpectatorHost; 23 | 24 | const createHost = createHostFactory({ 25 | component: StandaloneComponent, 26 | template: `
`, 27 | }); 28 | 29 | beforeEach(() => { 30 | host = createHost(); 31 | }); 32 | 33 | it('should render a StandaloneComponent', () => { 34 | expect(host.query('#standalone')).toContainText('This stands alone!'); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /projects/spectator/test/standalone/component/standalone.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: `app-standalone`, 5 | template: `
This stands alone!
`, 6 | standalone: true, 7 | }) 8 | export class StandaloneComponent {} 9 | -------------------------------------------------------------------------------- /projects/spectator/test/standalone/directive/standalone.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator'; 2 | import { StandaloneDirective } from './standalone.directive'; 3 | 4 | describe('StandaloneDirective', () => { 5 | describe('with SpectatorDirective', () => { 6 | let spectator: SpectatorDirective; 7 | 8 | const createDirective = createDirectiveFactory({ 9 | directive: StandaloneDirective, 10 | template: `
This stands alone!
`, 11 | }); 12 | 13 | beforeEach(() => { 14 | spectator = createDirective(); 15 | }); 16 | 17 | it('should render a StandaloneDirective', () => { 18 | expect(spectator.query("[class='btn']")).toContainText('This stands alone!'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /projects/spectator/test/standalone/directive/standalone.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostBinding } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: `[appStandalone]`, 5 | standalone: true, 6 | }) 7 | export class StandaloneDirective { 8 | @HostBinding() 9 | class = 'btn'; 10 | } 11 | -------------------------------------------------------------------------------- /projects/spectator/test/standalone/pipe/standalone.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'standalone', 5 | standalone: true, 6 | }) 7 | export class StandalonePipe implements PipeTransform { 8 | transform(value: string) { 9 | return `${value} stands alone!`; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/spectator/test/teardown/error.ts: -------------------------------------------------------------------------------- 1 | export class TeardownError extends Error { 2 | message = 'The error which is thrown during teardown'; 3 | } 4 | -------------------------------------------------------------------------------- /projects/spectator/test/teardown/teardown.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnDestroy } from '@angular/core'; 2 | 3 | import { TeardownError } from './error'; 4 | import { TeardownService } from './teardown.service'; 5 | 6 | @Component({ 7 | selector: 'app-teardown', 8 | template: '', 9 | standalone: false, 10 | }) 11 | export class TeardownComponent implements OnDestroy { 12 | @Input() 13 | rethrowErrors = false; 14 | 15 | constructor(readonly teardownService: TeardownService) {} 16 | 17 | ngOnDestroy(): void { 18 | if (this.rethrowErrors) { 19 | throw new TeardownError(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/test/teardown/teardown.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnDestroy } from '@angular/core'; 2 | 3 | @Injectable({ providedIn: 'root' }) 4 | export class TeardownService implements OnDestroy { 5 | ngOnDestroy(): void {} 6 | } 7 | -------------------------------------------------------------------------------- /projects/spectator/test/test.ts: -------------------------------------------------------------------------------- 1 | import { defineGlobalsInjections } from '../src/lib/globals-injections'; 2 | 3 | import { TranslatePipe } from './translate.pipe'; 4 | import { TranslateService } from './translate.service'; 5 | 6 | defineGlobalsInjections({ 7 | providers: [TranslateService], 8 | declarations: [TranslatePipe], 9 | }); 10 | -------------------------------------------------------------------------------- /projects/spectator/test/translate.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import { TranslateService } from './translate.service'; 4 | 5 | @Pipe({ 6 | name: 'translate', 7 | standalone: false, 8 | }) 9 | export class TranslatePipe implements PipeTransform { 10 | constructor(private readonly translateService: TranslateService) {} 11 | 12 | public transform(value: any, args?: any): any { 13 | return this.translateService.transform(value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /projects/spectator/test/translate.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class TranslateService { 5 | public transform(value: any): any { 6 | return value; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /projects/spectator/test/unless/unless.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator'; 2 | 3 | import { AppUnlessDirective } from './unless.component'; 4 | 5 | describe('HelloComponent', () => { 6 | let host: SpectatorHost; 7 | 8 | const createHost = createHostFactory(AppUnlessDirective); 9 | 10 | it('should work', () => { 11 | host = createHost(`
Hello world
`); 12 | expect(host.hostElement).toHaveText('Hello world'); 13 | }); 14 | 15 | it('should work', () => { 16 | host = createHost(`
Hello world
`); 17 | expect(host.hostElement).not.toHaveText('Hello world'); 18 | }); 19 | 20 | it('should use hostElement when using query to find element', () => { 21 | host = createHost(`
Hello world
`); 22 | expect(host.query('div')).not.toHaveText('Hello world'); 23 | }); 24 | 25 | it('should find the instance', () => { 26 | host = createHost(`
Hello world
`); 27 | expect(host.component).toEqual(jasmine.any(AppUnlessDirective)); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /projects/spectator/test/unless/unless.component.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[appUnless]', 5 | standalone: false, 6 | }) 7 | export class AppUnlessDirective { 8 | constructor( 9 | private readonly templateRef: TemplateRef, 10 | private readonly viewContainer: ViewContainerRef, 11 | ) {} 12 | 13 | @Input() public set appUnless(condition: boolean) { 14 | if (!condition) { 15 | this.viewContainer.createEmbeddedView(this.templateRef); 16 | } else { 17 | this.viewContainer.clear(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/spectator/test/view-children/view-children.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; 2 | 3 | import { ChildComponent } from '../child/child.component'; 4 | 5 | @Component({ 6 | selector: 'app-view-children', 7 | template: ` 8 |
9 | 10 | 11 | 12 | 13 |
By ref
14 |
By ref
15 | 16 | 17 |
18 | `, 19 | styles: [ 20 | ` 21 | :host { 22 | display: inline-block; 23 | } 24 | `, 25 | ], 26 | standalone: false, 27 | }) 28 | export class ViewChildrenComponent implements OnInit { 29 | @ViewChild(ChildComponent, { static: true }) public child!: ChildComponent; 30 | @ViewChildren(ChildComponent) public children!: QueryList; 31 | 32 | public ngOnInit(): void {} 33 | } 34 | -------------------------------------------------------------------------------- /projects/spectator/test/widget-data.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | 4 | @Injectable() 5 | export class WidgetDataService { 6 | constructor(private readonly http: HttpClient) {} 7 | 8 | public get(): void { 9 | return; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/spectator/test/widget.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; 2 | 3 | import { WidgetDataService } from './widget-data.service'; 4 | import { WidgetService } from './widget.service'; 5 | 6 | describe('WidgetService', () => { 7 | let spectator: SpectatorService; 8 | const createService = createServiceFactory({ 9 | service: WidgetService, 10 | mocks: [WidgetDataService], 11 | }); 12 | 13 | it('should be defined', () => { 14 | spectator = createService(); 15 | expect(spectator.service).toBeDefined(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /projects/spectator/test/widget.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { WidgetDataService } from './widget-data.service'; 4 | 5 | @Injectable() 6 | export class WidgetService { 7 | public testingProperty = 'hello'; 8 | 9 | constructor(private readonly dataService: WidgetDataService) {} 10 | 11 | public get(): void { 12 | return this.dataService.get(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/spectator/test/widget/widget.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost, SpyObject } from '@ngneat/spectator'; 2 | 3 | import { WidgetService } from '../widget.service'; 4 | 5 | import { WidgetComponent } from './widget.component'; 6 | 7 | describe('WidgetComponent', () => { 8 | let host: SpectatorHost; 9 | 10 | const createHost = createHostFactory({ 11 | component: WidgetComponent, 12 | mocks: [WidgetService], 13 | }); 14 | 15 | it('should work', () => { 16 | host = createHost(``); 17 | expect(host.component).toBeDefined(); 18 | }); 19 | 20 | it('should call the service method on button click', () => { 21 | host = createHost(``); 22 | host.click('button'); 23 | expect(host.inject(WidgetService).get).toHaveBeenCalled(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/test/widget/widget.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { WidgetService } from '../widget.service'; 4 | 5 | @Component({ 6 | selector: 'app-widget', 7 | template: ` `, 8 | styles: [], 9 | standalone: false, 10 | }) 11 | export class WidgetComponent implements OnInit { 12 | constructor(public widgetService: WidgetService) {} 13 | 14 | public ngOnInit(): void {} 15 | 16 | public onClick(): void { 17 | this.widgetService.get(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /projects/spectator/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "declarationMap": true, 6 | "declaration": true, 7 | "inlineSources": true, 8 | "typeRoots": [ 9 | "./typings", 10 | "../../node_modules/@types" 11 | ], 12 | "types": [ 13 | "jasmine", 14 | "jest", 15 | "node" 16 | ], 17 | "lib": [ 18 | "dom", 19 | "es2018" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "skipTemplateCodegen": true, 24 | "strictMetadataEmit": true, 25 | "fullTemplateTypeCheck": true, 26 | "strictInjectionParameters": true, 27 | "enableResourceInlining": true 28 | }, 29 | "exclude": [ 30 | "test/**/*.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/spectator/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "compilationMode": "partial" 8 | } 9 | } -------------------------------------------------------------------------------- /projects/spectator/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ], 9 | "baseUrl": ".", 10 | "paths": { 11 | "@ngneat/spectator": [ 12 | "src/public_api.ts" 13 | ], 14 | "@ngneat/spectator/jest": [ 15 | "jest/src/public_api.ts" 16 | ], 17 | "@ngneat/spectator/vitest": [ 18 | "vitest/src/public_api.ts" 19 | ], 20 | "@ngneat/spectator/internals": [ 21 | "internals/src/public_api.ts" 22 | ] 23 | } 24 | }, 25 | "files": [ 26 | "test/test.ts" 27 | ], 28 | "include": [ 29 | "test/**/*.spec.ts", 30 | "src/lib/matchers-types.ts", 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/spectator/typings/jest/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was creating to avoid conflicts during building between jasmine and jest. 3 | * 4 | * Projects using @ngneat/spectator should just use either @types/jasmine or @types/jest. 5 | */ 6 | 7 | declare namespace jest { 8 | interface Mock extends Function, MockInstance { 9 | new (...args: any[]): T; 10 | 11 | (...args: any[]): any; 12 | } 13 | 14 | function fn(implementation?: (...args: any[]) => any): Mock; 15 | 16 | interface MockInstance { 17 | mockImplementation(fn?: (...args: any[]) => any): Mock; 18 | 19 | mockReturnValue(value: any): Mock; 20 | 21 | mockReset(): void; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /projects/spectator/vite.config.mts: -------------------------------------------------------------------------------- 1 | // 2 | import { defineConfig } from 'vite'; 3 | import tsconfigPaths from 'vite-tsconfig-paths'; 4 | 5 | import angular from '@analogjs/vite-plugin-angular'; 6 | import * as path from 'node:path'; 7 | 8 | export default defineConfig(({ mode }) => ({ 9 | plugins: [ 10 | angular({tsconfig: path.join(import.meta.dirname, '/vitest/tsconfig.spec.json')}), 11 | tsconfigPaths() 12 | ], 13 | test: { 14 | globals: true, 15 | setupFiles: 'setup-vitest.ts', 16 | environment: 'jsdom', 17 | include: ['vitest/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], 18 | reporters: ['default'], 19 | pool: 'forks', 20 | }, 21 | define: { 22 | 'import.meta.vitest': mode !== 'production', 23 | }, 24 | })); 25 | -------------------------------------------------------------------------------- /projects/spectator/vitest/ng-package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /projects/spectator/vitest/src/lib/dom-selectors.ts: -------------------------------------------------------------------------------- 1 | export { byAltText, byLabel, byPlaceholder, byText, byTextContent, byTitle, byValue, byTestId, byRole } from '@ngneat/spectator'; 2 | -------------------------------------------------------------------------------- /projects/spectator/vitest/src/lib/spectator-http.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createHttpFactory as baseCreateHttpFactory, 4 | isType, 5 | CreateHttpOverrides, 6 | HttpMethod, 7 | SpectatorHttp as BaseSpectatorHttp, 8 | SpectatorHttpOptions, 9 | Token, 10 | } from '@ngneat/spectator'; 11 | 12 | import { mockProvider, SpyObject } from './mock'; 13 | 14 | /** 15 | * @publicApi 16 | */ 17 | export interface SpectatorHttp extends BaseSpectatorHttp { 18 | inject(token: Token): SpyObject; 19 | } 20 | 21 | /** 22 | * @publicApi 23 | */ 24 | export { HttpMethod }; 25 | 26 | /** 27 | * @pubicApi 28 | */ 29 | export type SpectatorHttpFactory = (overrides?: CreateHttpOverrides) => SpectatorHttp; 30 | 31 | /** 32 | * @publicApi 33 | */ 34 | export function createHttpFactory(typeOrOptions: SpectatorHttpOptions | Type): SpectatorHttpFactory { 35 | return baseCreateHttpFactory({ 36 | mockProvider, 37 | ...(isType(typeOrOptions) ? { service: typeOrOptions } : typeOrOptions), 38 | }) as SpectatorHttpFactory; 39 | } 40 | -------------------------------------------------------------------------------- /projects/spectator/vitest/src/lib/spectator-injection-context.ts: -------------------------------------------------------------------------------- 1 | import { AbstractType, InjectionToken, Type } from '@angular/core'; 2 | import { 3 | createInjectionContextFactory as baseInjectionContextFactory, 4 | SpectatorInjectionContextOverrides, 5 | SpectatorInjectionContextOptions, 6 | SpectatorInjectionContext as BaseSpectatorInjectionContext, 7 | } from '@ngneat/spectator'; 8 | import { mockProvider, SpyObject } from './mock'; 9 | 10 | /** 11 | * @publicApi 12 | */ 13 | export interface SpectatorInjectionContext extends BaseSpectatorInjectionContext { 14 | inject(token: Type | InjectionToken | AbstractType): SpyObject; 15 | } 16 | 17 | /** 18 | * @publicApi 19 | */ 20 | export type SpectatorInjectionContextFactory = (overrides?: SpectatorInjectionContextOverrides) => SpectatorInjectionContext; 21 | 22 | /** 23 | * @publicApi 24 | */ 25 | export function createInjectionContextFactory(options: SpectatorInjectionContextOptions): SpectatorInjectionContextFactory { 26 | return baseInjectionContextFactory({ 27 | mockProvider, 28 | ...options, 29 | }) as SpectatorInjectionContextFactory; 30 | } 31 | -------------------------------------------------------------------------------- /projects/spectator/vitest/src/lib/spectator-pipe.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createPipeFactory as baseCreatePipeFactory, 4 | isType, 5 | HostComponent, 6 | SpectatorPipe as BaseSpectatorPipe, 7 | SpectatorPipeOptions, 8 | SpectatorPipeOverrides, 9 | Token, 10 | } from '@ngneat/spectator'; 11 | 12 | import { mockProvider, SpyObject } from './mock'; 13 | 14 | /** 15 | * @publicApi 16 | */ 17 | export class SpectatorPipe extends BaseSpectatorPipe { 18 | public inject(token: Token): SpyObject { 19 | return super.inject(token) as SpyObject; 20 | } 21 | } 22 | 23 | /** 24 | * @publicApi 25 | */ 26 | export type SpectatorPipeFactory = ( 27 | templateOrOverrides?: string | SpectatorPipeOverrides, 28 | overrides?: SpectatorPipeOverrides, 29 | ) => SpectatorPipe; 30 | 31 | /** 32 | * @publicApi 33 | */ 34 | export function createPipeFactory(typeOrOptions: Type

| SpectatorPipeOptions): SpectatorPipeFactory { 35 | return baseCreatePipeFactory({ 36 | mockProvider, 37 | ...(isType(typeOrOptions) ? { pipe: typeOrOptions } : typeOrOptions), 38 | }) as SpectatorPipeFactory; 39 | } 40 | -------------------------------------------------------------------------------- /projects/spectator/vitest/src/lib/spectator-routing.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createRoutingFactory as baseCreateRoutingFactory, 4 | isType, 5 | SpectatorRouting as BaseSpectatorRouting, 6 | SpectatorRoutingOptions, 7 | SpectatorRoutingOverrides, 8 | Token, 9 | } from '@ngneat/spectator'; 10 | 11 | import { mockProvider, SpyObject } from './mock'; 12 | 13 | /** 14 | * @publicApi 15 | */ 16 | export class SpectatorRouting extends BaseSpectatorRouting { 17 | public inject(token: Token, fromComponentInjector: boolean = false): SpyObject { 18 | return super.inject(token, fromComponentInjector) as SpyObject; 19 | } 20 | } 21 | 22 | /** 23 | * @publicApi 24 | */ 25 | export type SpectatorRoutingFactory = (overrides?: SpectatorRoutingOverrides) => SpectatorRouting; 26 | 27 | /** 28 | * @publicApi 29 | */ 30 | export function createRoutingFactory(typeOrOptions: SpectatorRoutingOptions | Type): SpectatorRoutingFactory { 31 | return baseCreateRoutingFactory({ 32 | mockProvider, 33 | ...(isType(typeOrOptions) ? { component: typeOrOptions } : typeOrOptions), 34 | }) as SpectatorRoutingFactory; 35 | } 36 | -------------------------------------------------------------------------------- /projects/spectator/vitest/src/lib/spectator-service.ts: -------------------------------------------------------------------------------- 1 | import { Type, InjectionToken, AbstractType } from '@angular/core'; 2 | import { 3 | createServiceFactory as baseCreateServiceFactory, 4 | isType, 5 | SpectatorServiceOverrides, 6 | SpectatorServiceOptions, 7 | SpectatorService as BaseSpectatorService, 8 | Token, 9 | } from '@ngneat/spectator'; 10 | 11 | import { mockProvider, SpyObject } from './mock'; 12 | 13 | /** 14 | * @publicApi 15 | */ 16 | export interface SpectatorService extends BaseSpectatorService { 17 | inject(token: Type | InjectionToken | AbstractType): SpyObject; 18 | } 19 | 20 | /** 21 | * @publicApi 22 | */ 23 | export type SpectatorServiceFactory = (overrides?: SpectatorServiceOverrides) => SpectatorService; 24 | 25 | /** 26 | * @publicApi 27 | */ 28 | export function createServiceFactory(typeOrOptions: SpectatorServiceOptions | Type): SpectatorServiceFactory { 29 | return baseCreateServiceFactory({ 30 | mockProvider, 31 | ...(isType(typeOrOptions) ? { service: typeOrOptions } : typeOrOptions), 32 | }) as SpectatorServiceFactory; 33 | } 34 | -------------------------------------------------------------------------------- /projects/spectator/vitest/src/lib/spectator.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { 3 | createComponentFactory as baseCreateComponentFactory, 4 | isType, 5 | Spectator as BaseSpectator, 6 | SpectatorOptions, 7 | SpectatorOverrides, 8 | Token, 9 | } from '@ngneat/spectator'; 10 | 11 | import { mockProvider, SpyObject } from './mock'; 12 | 13 | /** 14 | * @publicApi 15 | */ 16 | export type SpectatorFactory = (options?: SpectatorOverrides) => Spectator; 17 | 18 | export function createComponentFactory(typeOrOptions: SpectatorOptions | Type): SpectatorFactory { 19 | return baseCreateComponentFactory({ 20 | mockProvider, 21 | ...(isType(typeOrOptions) ? { component: typeOrOptions } : typeOrOptions), 22 | }) as SpectatorFactory; 23 | } 24 | 25 | export class Spectator extends BaseSpectator { 26 | public inject(token: Token, fromComponentInjector: boolean = false): SpyObject { 27 | return super.inject(token, fromComponentInjector) as SpyObject; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /projects/spectator/vitest/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | export * from './lib/dom-selectors'; 4 | export * from './lib/mock'; 5 | export * from './lib/spectator'; 6 | export * from './lib/spectator-http'; 7 | export * from './lib/spectator-directive'; 8 | export * from './lib/spectator-service'; 9 | export * from './lib/spectator-host'; 10 | export * from './lib/spectator-routing'; 11 | export * from './lib/spectator-pipe'; 12 | export * from './lib/spectator-injection-context'; 13 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/async-input/async-input.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync } from '@angular/core/testing'; 2 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; 3 | 4 | import { AsyncInputComponent } from '../../../test/async-input/async-input.component'; 5 | 6 | describe('ZippyComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory(AsyncInputComponent); 10 | 11 | it('should work', () => { 12 | const { component } = createHost(``); 13 | expect(component).toBeDefined(); 14 | }); 15 | 16 | it('should not be visible', () => { 17 | host = createHost(``); 18 | host.setHostInput('widgets', ''); 19 | expect(host.query('div')).not.toExist(); 20 | }); 21 | 22 | it('should be visible', fakeAsync(() => { 23 | host = createHost(``, { 24 | detectChanges: true, 25 | hostProps: { 26 | widgets: '', 27 | }, 28 | }); 29 | host.tick(); 30 | host.detectChanges(); 31 | expect(host.query('div')).toExist(); 32 | })); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/async/async.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; 2 | 3 | import { AsyncComponent } from '../../../test/async/async.component'; 4 | import { QueryService } from '../../../test/query.service'; 5 | 6 | describe('ZippyComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory({ 10 | component: AsyncComponent, 11 | mocks: [QueryService], 12 | }); 13 | 14 | it('should work', () => { 15 | const { component } = createHost(``); 16 | expect(component).toBeDefined(); 17 | }); 18 | 19 | it('should be falsy', () => { 20 | host = createHost(``); 21 | expect(host.query('p')).not.toExist(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, SpectatorService } from '@ngneat/spectator/vitest'; 2 | 3 | import { AuthService } from '../../test/auth.service'; 4 | import { DateService } from '../../test/date.service'; 5 | 6 | describe('AuthService', () => { 7 | it('should ', () => { 8 | expect(true).toBeTruthy(); 9 | }); 10 | 11 | let spectator: SpectatorService; 12 | const createService = createServiceFactory({ 13 | service: AuthService, 14 | mocks: [DateService], 15 | }); 16 | 17 | beforeEach(() => (spectator = createService())); 18 | 19 | it('should not be logged in', () => { 20 | const dateService = spectator.inject(DateService); 21 | dateService.isExpired.mockReturnValue(true); 22 | expect(spectator.service.isLoggedIn()).toBeFalsy(); 23 | }); 24 | 25 | it('should be logged in', () => { 26 | const dateService = spectator.inject(DateService); 27 | dateService.isExpired.mockReturnValue(false); 28 | expect(spectator.service.isLoggedIn()).toBeTruthy(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/calc.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; 2 | 3 | import { CalcComponent } from '../../test/calc/calc.component'; 4 | 5 | describe('CalcComponent', () => { 6 | let spectator: Spectator; 7 | const createComponent = createComponentFactory(CalcComponent); 8 | 9 | it('should be defined', () => { 10 | spectator = createComponent(); 11 | expect(spectator.component).toBeTruthy(); 12 | }); 13 | 14 | it('should calc the value', () => { 15 | spectator = createComponent(); 16 | const a = spectator.query('.a') as HTMLInputElement; 17 | const b = spectator.query('.b') as HTMLInputElement; 18 | spectator.typeInElement('1', a); 19 | spectator.typeInElement('2', b); 20 | 21 | expect(spectator.query('.result')).toHaveText('12'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/click/click.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { fakeAsync } from '@angular/core/testing'; 2 | import { byText, createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; 3 | import { ClickComponent } from '../../../test/click/click.component'; 4 | 5 | describe('ClickComponent', () => { 6 | let component: ClickComponent; 7 | let spectator: Spectator; 8 | 9 | const createComponent = createComponentFactory(ClickComponent); 10 | 11 | beforeEach(() => { 12 | spectator = createComponent(); 13 | component = spectator.component; 14 | }); 15 | 16 | it('should create', () => { 17 | expect(component).toBeTruthy(); 18 | }); 19 | 20 | it('should changed on click with click query shorthand', fakeAsync(() => { 21 | spectator.click('button'); 22 | spectator.tick(100); 23 | 24 | expect('p').toHaveText('changed'); 25 | })); 26 | 27 | it('should changed on click with click dom selector', fakeAsync(() => { 28 | spectator.click(byText('Change')); 29 | spectator.tick(100); 30 | 31 | expect('p').toHaveText('changed'); 32 | })); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/consum-dynamic/consume-dynamic.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; 2 | 3 | import { ConsumeDynamicComponent } from '../../../test/consum-dynamic/consume-dynamic.component'; 4 | import { DynamicComponent } from '../../../test/dynamic/dynamic.component'; 5 | 6 | describe('ConsumeDynamicComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory({ 10 | declarations: [DynamicComponent], 11 | entryComponents: [DynamicComponent], 12 | component: ConsumeDynamicComponent, 13 | }); 14 | 15 | it('should work', () => { 16 | host = createHost(``); 17 | expect(host.component).toBeDefined(); 18 | }); 19 | 20 | it('should render the dynamic component', () => { 21 | host = createHost(``); 22 | expect(host.queryHost('.dynamic')).toHaveText('dynamic works!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/consumer.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/vitest'; 2 | import { Subject } from 'rxjs'; 3 | 4 | import { ConsumerService } from '../../test/consumer.service'; 5 | import { ProviderService } from '../../test/provider.service'; 6 | 7 | describe('ConsumerService', () => { 8 | const randomNumber = Math.random(); 9 | 10 | let spectator: SpectatorService; 11 | const createService = createServiceFactory({ 12 | service: ConsumerService, 13 | providers: [ 14 | mockProvider(ProviderService, { 15 | obs$: new Subject(), 16 | method: () => randomNumber, 17 | }), 18 | ], 19 | }); 20 | 21 | beforeEach(() => (spectator = createService())); 22 | 23 | it('should consume mocked service with properties', () => { 24 | const provider = spectator.inject(ProviderService); 25 | expect(spectator.service.lastValue).toBeUndefined(); 26 | provider.obs$.next('hey you'); 27 | expect(spectator.service.lastValue).toBe('hey you'); 28 | }); 29 | 30 | it('should consume mocked service methods', () => { 31 | expect(spectator.service.consumeProvider()).toBe(randomNumber); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/fg/fg.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { SpectatorHost, createHostFactory } from '@ngneat/spectator/vitest'; 2 | import { Component } from '@angular/core'; 3 | import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; 4 | 5 | import { FgComponent } from '../../../test/fg/fg.component'; 6 | 7 | @Component({ 8 | selector: 'app-custom-host', 9 | template: '', 10 | standalone: false, 11 | }) 12 | class CustomHostComponent { 13 | public group = new FormGroup({ 14 | name: new FormControl('name'), 15 | }); 16 | } 17 | 18 | describe('With Custom Host Component', () => { 19 | let host: SpectatorHost; 20 | 21 | const createHost = createHostFactory({ 22 | component: FgComponent, 23 | imports: [ReactiveFormsModule], 24 | host: CustomHostComponent, 25 | }); 26 | 27 | it('should display the host component title', () => { 28 | host = createHost(``); 29 | expect(host.component).toBeDefined(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/form-select/form-select.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFormsModule } from '@angular/forms'; 2 | import { Spectator, createComponentFactory } from '@ngneat/spectator/vitest'; 3 | 4 | import { FormSelectComponent } from './form-select.component'; 5 | 6 | describe('FormSelectComponent', () => { 7 | let spectator: Spectator; 8 | 9 | const createComponent = createComponentFactory({ 10 | component: FormSelectComponent, 11 | imports: [ReactiveFormsModule], 12 | }); 13 | 14 | beforeEach(() => (spectator = createComponent())); 15 | 16 | it('should set the correct option on standard select', () => { 17 | // const select = spectator.query('#test-single-select') as HTMLSelectElement; 18 | // spectator.selectOption(select, '1'); 19 | // expect(select).toHaveSelectedOptions('1'); 20 | expect(true).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/form-select/form-select.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ChangeDetectionStrategy } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-form-select', 5 | template: ` 6 | 11 | `, 12 | changeDetection: ChangeDetectionStrategy.OnPush, 13 | standalone: false, 14 | }) 15 | export class FormSelectComponent { 16 | /** 17 | * Empty method to spy on 18 | */ 19 | public handleChange(): void { 20 | return; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/mock.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockProvider } from '@ngneat/spectator/vitest'; 2 | 3 | import { WidgetService } from '../../test/widget.service'; 4 | 5 | describe('mockProvider', () => { 6 | it('should not modify the object passed in 2nd argument when running the mock factory', () => { 7 | const customPropertiesAndMethods: Partial> = { 8 | testingProperty: 'overriden', 9 | }; 10 | const { useFactory: factory } = mockProvider(WidgetService, customPropertiesAndMethods); 11 | factory(); 12 | expect(customPropertiesAndMethods).toEqual({ 13 | testingProperty: 'overriden', 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/ngonchanges-input/ngonchanges-input.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest'; 2 | import { NgOnChangesInputComponent } from '../../../test/ngonchanges-input/ngonchanges-input.component'; 3 | 4 | describe('NgOnChangesInputComponent', () => { 5 | describe('with Spectator', () => { 6 | let spectator: Spectator; 7 | 8 | const createComponent = createComponentFactory({ 9 | component: NgOnChangesInputComponent, 10 | }); 11 | 12 | beforeEach(() => { 13 | spectator = createComponent(); 14 | }); 15 | 16 | it('should re-render when updating fields in ngOnChanges', () => { 17 | expect(spectator.query('button')).toBeDisabled(); 18 | expect(spectator.query('button')).toHaveText('Button disabled'); 19 | 20 | spectator.setInput({ btnDisabled: false }); 21 | expect(spectator.query('button')).not.toBeDisabled(); 22 | expect(spectator.query('button')).toHaveText('Button enabled'); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/standalone/component/standalone.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createComponentFactory, createHostFactory, Spectator, SpectatorHost } from '@ngneat/spectator/vitest'; 2 | import { StandaloneComponent } from '../../../../test/standalone/component/standalone.component'; 3 | 4 | describe('StandaloneComponent', () => { 5 | describe('with Spectator', () => { 6 | let spectator: Spectator; 7 | 8 | const createComponent = createComponentFactory({ 9 | component: StandaloneComponent, 10 | }); 11 | 12 | beforeEach(() => { 13 | spectator = createComponent(); 14 | }); 15 | 16 | it('should render a StandaloneComponent', () => { 17 | expect(spectator.query('#standalone')).toContainText('This stands alone!'); 18 | }); 19 | }); 20 | 21 | describe('with SpectatorHost', () => { 22 | let host: SpectatorHost; 23 | 24 | const createHost = createHostFactory({ 25 | component: StandaloneComponent, 26 | template: `

`, 27 | }); 28 | 29 | beforeEach(() => { 30 | host = createHost(); 31 | }); 32 | 33 | it('should render a StandaloneComponent', () => { 34 | expect(host.query('#standalone')).toContainText('This stands alone!'); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/standalone/directive/standalone.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/vitest'; 2 | import { StandaloneDirective } from '../../../../test/standalone/directive/standalone.directive'; 3 | 4 | describe('StandaloneDirective', () => { 5 | describe('with SpectatorDirective', () => { 6 | let spectator: SpectatorDirective; 7 | 8 | const createDirective = createDirectiveFactory({ 9 | directive: StandaloneDirective, 10 | template: `
This stands alone!
`, 11 | }); 12 | 13 | beforeEach(() => { 14 | spectator = createDirective(); 15 | }); 16 | 17 | it('should render a StandaloneDirective', () => { 18 | expect(spectator.query("[class='btn']")).toContainText('This stands alone!'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/teardown/error.ts: -------------------------------------------------------------------------------- 1 | export class TeardownError extends Error { 2 | message = 'The error which is thrown during teardown'; 3 | } 4 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/teardown/teardown.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnDestroy } from '@angular/core'; 2 | 3 | import { TeardownError } from './error'; 4 | import { TeardownService } from './teardown.service'; 5 | 6 | @Component({ 7 | selector: 'app-teardown', 8 | template: '', 9 | standalone: false, 10 | }) 11 | export class TeardownComponent implements OnDestroy { 12 | @Input() 13 | rethrowErrors = false; 14 | 15 | constructor(readonly teardownService: TeardownService) {} 16 | 17 | ngOnDestroy(): void { 18 | if (this.rethrowErrors) { 19 | throw new TeardownError(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/teardown/teardown.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnDestroy } from '@angular/core'; 2 | 3 | @Injectable({ providedIn: 'root' }) 4 | export class TeardownService implements OnDestroy { 5 | ngOnDestroy(): void {} 6 | } 7 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/unless/unless.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; 2 | 3 | import { AppUnlessDirective } from '../../../test/unless/unless.component'; 4 | 5 | describe('HelloComponent', () => { 6 | let host: SpectatorHost; 7 | 8 | const createHost = createHostFactory(AppUnlessDirective); 9 | 10 | it('should work', () => { 11 | host = createHost(`
Hello world
`); 12 | expect(host.hostElement).toHaveText('Hello world'); 13 | }); 14 | 15 | it('should work', () => { 16 | host = createHost(`
Hello world
`); 17 | expect(host.hostElement).not.toHaveText('Hello world'); 18 | }); 19 | 20 | it('should use hostElement when using query to find element', () => { 21 | host = createHost(`
Hello world
`); 22 | expect(host.query('div')).not.toHaveText('Hello world'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/widget.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { createServiceFactory, SpectatorService } from '@ngneat/spectator/vitest'; 2 | 3 | import { WidgetDataService } from '../../test/widget-data.service'; 4 | import { WidgetService } from '../../test/widget.service'; 5 | 6 | describe('WidgetService', () => { 7 | let spectator: SpectatorService; 8 | const createService = createServiceFactory({ 9 | service: WidgetService, 10 | mocks: [WidgetDataService], 11 | }); 12 | 13 | beforeEach(() => (spectator = createService())); 14 | 15 | it('should be defined', () => { 16 | expect(spectator.service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /projects/spectator/vitest/test/widget/widget.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { createHostFactory, SpectatorHost } from '@ngneat/spectator/vitest'; 2 | 3 | import { WidgetComponent } from '../../../test/widget/widget.component'; 4 | import { WidgetService } from '../../../test/widget.service'; 5 | 6 | describe('WidgetComponent', () => { 7 | let host: SpectatorHost; 8 | 9 | const createHost = createHostFactory({ 10 | component: WidgetComponent, 11 | mocks: [WidgetService], 12 | }); 13 | 14 | it('should work', () => { 15 | host = createHost(``); 16 | expect(host.component).toBeDefined(); 17 | }); 18 | 19 | it('should call the service method on button click', () => { 20 | host = createHost(``); 21 | host.click('button'); 22 | const widgetService = host.component.widgetService; 23 | expect(widgetService.get).toHaveBeenCalled(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/spectator/vitest/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.spec.json", 3 | "include": [ 4 | "./test/**/*.spec.ts", 5 | "./src/lib/matchers-types.ts" 6 | ], 7 | "files": [ 8 | "../setup-vitest.ts" 9 | ], 10 | "compilerOptions": { 11 | "types": [ 12 | "node", 13 | "vitest", 14 | "@vitest/globals" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2020", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "strict": true, 14 | "noImplicitAny": false, 15 | "target": "ES2022", 16 | "esModuleInterop": true, 17 | "typeRoots": [ 18 | "node_modules/@types" 19 | ], 20 | "lib": [ 21 | "es2018", 22 | "dom" 23 | ], 24 | "paths": { 25 | "@ngneat/spectator": [ 26 | "projects/spectator/src/public_api.ts" 27 | ], 28 | "@ngneat/spectator/jest": [ 29 | "projects/spectator/jest/src/public_api.ts" 30 | ], 31 | "@ngneat/spectator/vitest": [ 32 | "projects/spectator/vitest/src/public_api.ts" 33 | ], 34 | "@ngneat/spectator/internals": [ 35 | "projects/spectator/internals/src/public_api.ts" 36 | ], 37 | // workaround for: https://github.com/rollup/rollup/issues/5199 38 | "rollup/parseAst": ["./node_modules/rollup/dist/parseAst"] 39 | }, 40 | "useDefineForClassFields": false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vscode-snippets/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "spectator" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /vscode-snippets/README.md: -------------------------------------------------------------------------------- 1 | ## Code snippets for @ngneat/spectator 2 | 3 | #### Available snippets: 4 | 5 | ```ts 6 | spec-comp 7 | spec-host 8 | spec-directive 9 | spec-service 10 | spec-http 11 | spec-routing 12 | spec-jest-comp 13 | spec-jest-host 14 | spec-jest-directive 15 | spec-jest-service 16 | spec-jest-http 17 | spec-jest-routing 18 | ``` -------------------------------------------------------------------------------- /vscode-snippets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngneat/spectator/be6f2a89c1d11b1b38c8bb2af887405bb7e73ee9/vscode-snippets/logo.png -------------------------------------------------------------------------------- /vscode-snippets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spectator-snippets", 3 | "displayName": "Angular Spectator Snippets", 4 | "description": "Code snippets for Angular Spectator", 5 | "version": "0.0.2", 6 | "publisher": "NetanelBasal", 7 | "icon": "logo.png", 8 | "engines": { 9 | "vscode": "^1.30.0" 10 | }, 11 | "galleryBanner": { 12 | "color": "#BDE4A7" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/ngneat/spectator" 17 | }, 18 | "categories": [ 19 | "Snippets" 20 | ], 21 | "contributes": { 22 | "snippets": [ 23 | { 24 | "language": "typescript", 25 | "path": "./snippets/snippets.json" 26 | } 27 | ] 28 | } 29 | } -------------------------------------------------------------------------------- /vscode-snippets/vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file that defines the location of the snippet file and specifies the language of the snippets. 7 | * `snippets/snippets.json` - the file containing all snippets. 8 | 9 | ## Get up and running straight away 10 | 11 | * Press `F5` to open a new window with your extension loaded. 12 | * Create a new file with a file name suffix matching your language. 13 | * Verify that your snippets are proposed on intellisense. 14 | 15 | ## Make changes 16 | 17 | * You can relaunch the extension from the debug toolbar after making changes to the files listed above. 18 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 19 | 20 | ## Install your extension 21 | 22 | * To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code. 23 | * To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension. 24 | --------------------------------------------------------------------------------