@nutin/cli 1.3.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -9
- package/README.md +17 -35
- package/bin/add-feature.mjs +9 -0
- package/bin/cli.mjs +8 -0
- package/bin/create-app.mjs +5 -4
- package/bin/update-app.mjs +9 -0
- package/lib/index.mjs +76 -25
- package/lib/src/add-feature/feature-adder.mjs +83 -0
- package/lib/src/add-feature/feature-context-builder.mjs +25 -0
- package/lib/src/add-feature/nutin-config-updater.mjs +29 -0
- package/lib/src/add-feature/package-json-updater.mjs +49 -0
- package/lib/src/common/feature-registry.mjs +11 -0
- package/lib/src/common/file-generator.mjs +159 -0
- package/lib/src/common/meta-bootstrap-prompt.mjs +33 -0
- package/lib/src/common/package-data.mjs +10 -0
- package/lib/src/common/package-json-helper.mjs +103 -0
- package/lib/src/common/project-meta.mjs +54 -0
- package/lib/src/create-app/context-builder.mjs +21 -0
- package/lib/src/{git-manager.mjs → create-app/git-manager.mjs} +2 -2
- package/lib/src/create-app/json-manager.mjs +73 -0
- package/lib/src/{prompts.mjs → create-app/new-app-prompt.mjs} +6 -6
- package/lib/src/{project-generator.mjs → create-app/project-generator.mjs} +10 -8
- package/lib/src/create-app/success-message.mjs +13 -0
- package/lib/src/update-app/conflict-report-writer.mjs +31 -0
- package/lib/src/update-app/fetch-old-templates.mjs +61 -0
- package/lib/src/update-app/project-updater.mjs +142 -0
- package/lib/src/update-app/template-differ.mjs +124 -0
- package/lib/src/update-app/update-context-builder.mjs +25 -0
- package/lib/src/update-app/update-summary-printer.mjs +22 -0
- package/lib/src/update-app/version-compare.mjs +12 -0
- package/lib/src/utils/print.mjs +36 -0
- package/lib/src/utils/promise-exec-alias.mjs +4 -0
- package/package.json +17 -12
- package/templates/base/.gitignore.hbs +6 -1
- package/templates/base/AGENTS.md.hbs +168 -0
- package/templates/base/GETTING_STARTED.md.hbs +199 -0
- package/templates/base/README.md.hbs +8 -17
- package/templates/base/config/languages.json.hbs +6 -0
- package/templates/base/config/seo.json.hbs +12 -0
- package/templates/base/nutin.config.js.hbs +44 -0
- package/templates/base/src/app/components/index.ts.hbs +1 -2
- package/templates/base/src/app/globals.d.ts.hbs +3 -19
- package/templates/base/src/{libs/guards → app}/guards.ts.hbs +1 -5
- package/templates/base/src/app/main.ts.hbs +6 -26
- package/templates/base/src/app/services/index.ts.hbs +1 -1
- package/templates/base/src/app/views/home/home.view.html.hbs +7 -0
- package/templates/base/src/app/views/home/home.view.ts.hbs +2 -57
- package/templates/base/src/app/views/index.ts.hbs +1 -0
- package/templates/base/src/app/views/not-found/not-found.view.html.hbs +8 -0
- package/templates/base/src/app/views/not-found/not-found.view.ts.hbs +5 -38
- package/templates/base/src/core/base-classes/base-component/base-component.test.js.hbs +158 -0
- package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +68 -35
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.test.js.hbs +124 -0
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +9 -8
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.test.js.hbs +96 -0
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +6 -3
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.test.js.hbs +151 -0
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +8 -2
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.test.js.hbs +88 -0
- package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +2 -2
- package/templates/base/src/core/base-classes/base-component/helpers/i18n.helper.test.js.hbs +62 -0
- package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.test.js.hbs +89 -0
- package/templates/base/src/core/base-classes/base-component/helpers/pipe.helper.ts.hbs +4 -1
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.test.js.hbs +142 -0
- package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +67 -22
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.test.js.hbs +183 -0
- package/templates/base/src/core/base-classes/component/component.test.js.hbs +85 -0
- package/templates/base/src/core/base-classes/component/component.ts.hbs +24 -33
- package/templates/base/src/core/base-classes/component/helpers/config.helper.test.js.hbs +58 -0
- package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.test.js.hbs +75 -0
- package/templates/base/src/core/base-classes/service/service.test.js.hbs +150 -0
- package/templates/base/src/core/base-classes/service/service.ts.hbs +26 -20
- package/templates/base/src/core/base-classes/view/view.test.js.hbs +71 -0
- package/templates/base/src/core/base-classes/view/view.ts.hbs +24 -49
- package/templates/base/src/core/config.ts.hbs +10 -0
- package/templates/base/src/core/index.ts.hbs +0 -3
- package/templates/base/src/core/internals.d.ts.hbs +27 -0
- package/templates/base/src/core/nutin-config.d.ts.hbs +4 -0
- package/templates/base/src/core/services/event-bus/event-bus.test.js.hbs +67 -3
- package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +9 -4
- package/templates/base/src/core/services/event-bus/index.ts.hbs +3 -0
- package/templates/base/src/core/services/event-bus/lifecycle.facade.test.js.hbs +123 -0
- package/templates/base/src/core/services/event-bus/lifecycle.facade.ts.hbs +61 -0
- package/templates/base/src/core/services/event-bus/navigation.facade.test.js.hbs +46 -0
- package/templates/base/src/core/services/event-bus/navigation.facade.ts.hbs +25 -0
- package/templates/base/src/core/services/globals/globals.test.js.hbs +187 -0
- package/templates/base/src/core/services/globals/globals.ts.hbs +104 -0
- package/templates/base/src/core/services/http-client/helpers/http-builder.helper.test.js.hbs +25 -0
- package/templates/base/src/core/services/http-client/helpers/http-manager.helper.test.js.hbs +20 -0
- package/templates/base/src/core/services/http-client/http-client.test.js.hbs +73 -2
- package/templates/base/src/core/services/http-client/http-client.ts.hbs +4 -1
- package/templates/base/src/core/services/i18n/i18n.test.js.hbs +270 -0
- package/templates/{features/i18n → base}/src/core/services/i18n/i18n.ts.hbs +22 -4
- package/templates/base/src/core/services/index.ts.hbs +5 -6
- package/templates/base/src/core/services/pipe-registry/pipe-registry.test.js.hbs +60 -1
- package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +1 -1
- package/templates/base/src/core/services/pipe-registry/pipes.test.js.hbs +128 -0
- package/templates/base/src/{libs/pipes → core/services/pipe-registry}/pipes.ts.hbs +8 -5
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.test.js.hbs +257 -6
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +56 -29
- package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.test.js.hbs +27 -0
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.test.js.hbs +83 -17
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +14 -38
- package/templates/base/src/core/services/router/router.test.js.hbs +304 -2
- package/templates/base/src/core/services/router/router.ts.hbs +47 -12
- package/templates/base/src/index.html.hbs +2 -6
- package/templates/base/src/styles/_styles.scss.hbs +102 -43
- package/templates/base/src/styles/base/_variables.scss.hbs +1 -86
- package/templates/base/src/styles/main.scss.hbs +1 -18
- package/templates/base/src/styles/tailwind.css.hbs +2 -0
- package/templates/base/tools/builder/builder.config.js.hbs +40 -0
- package/templates/base/tools/builder/builder.js.hbs +24 -26
- package/templates/base/tools/builder/core/app/binary-extensions.test.js.hbs +16 -0
- package/templates/base/tools/builder/core/app/compile-ts.js.hbs +16 -0
- package/templates/base/tools/builder/core/{copy-static.js.hbs → app/copy-static.js.hbs} +27 -34
- package/templates/base/tools/builder/core/app/validate-routes.js.hbs +77 -0
- package/templates/base/tools/builder/core/finalize-build.js.hbs +16 -7
- package/templates/base/tools/builder/core/{add-tags.js.hbs → html-index/add-tags.js.hbs} +2 -2
- package/templates/base/tools/builder/core/html-index/add-tags.test.js.hbs +56 -0
- package/templates/base/tools/builder/core/{validate-html.js.hbs → html-index/validate-html.js.hbs} +5 -11
- package/templates/base/tools/builder/core/html-templates/merge-templates.js.hbs +152 -0
- package/templates/base/tools/builder/core/html-templates/minify-html.js.hbs +15 -0
- package/templates/base/tools/builder/core/html-templates/process-html-templates.js.hbs +8 -0
- package/templates/{features/i18n/tools/builder/core → base/tools/builder/core/i18n}/build-i18n.js.hbs +6 -17
- package/templates/base/tools/builder/core/prod-bundle/compress-files.js.hbs +37 -0
- package/templates/base/tools/builder/core/prod-bundle/esbuild.js.hbs +30 -0
- package/templates/base/tools/builder/core/{hash-files.js.hbs → prod-bundle/hash-files.js.hbs} +14 -11
- package/templates/base/tools/builder/core/seo/generate-robots-txt.js.hbs +51 -0
- package/templates/base/tools/builder/core/seo/generate-seo-files.js.hbs +16 -0
- package/templates/base/tools/builder/core/seo/generate-seo-html.js.hbs +128 -0
- package/templates/base/tools/builder/core/seo/generate-sitemap-xml.js.hbs +43 -0
- package/templates/base/tools/builder/core/seo/html-substitutions.js.hbs +99 -0
- package/templates/base/tools/builder/core/seo/i18n-resolution.js.hbs +66 -0
- package/templates/base/tools/builder/core/seo/route-output.js.hbs +64 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-bundle.js.hbs +54 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-polyfills.js.hbs +132 -0
- package/templates/base/tools/builder/core/seo/ssr/ssr-render.js.hbs +95 -0
- package/templates/base/tools/builder/core/styles/sass.js.hbs +40 -0
- package/templates/base/tools/builder/core/styles/tailwind.js.hbs +84 -0
- package/templates/base/tools/dev/dev-serve.js.hbs +26 -18
- package/templates/base/tools/dev/serve.js.hbs +11 -1
- package/templates/base/tools/dev/watcher.js.hbs +14 -6
- package/templates/base/tools/generator/generator.js.hbs +44 -47
- package/templates/base/tools/generator/handle-file.js.hbs +17 -44
- package/templates/base/tools/generator/handle-file.test.js.hbs +72 -0
- package/templates/base/tools/generator/templates/{component.template.js.hbs → component/component.template.js.hbs} +3 -2
- package/templates/base/tools/generator/templates/component/component.template.test.js.hbs +23 -0
- package/templates/base/tools/generator/templates/html/html.template.js.hbs +2 -0
- package/templates/base/tools/generator/templates/html/html.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/index.js.hbs +7 -12
- package/templates/base/tools/generator/templates/locale/locale.template.js.hbs +8 -0
- package/templates/base/tools/generator/templates/locale/locale.template.test.js.hbs +17 -0
- package/templates/base/tools/generator/templates/scss/scss.template.js.hbs +1 -0
- package/templates/base/tools/generator/templates/scss/scss.template.test.js.hbs +7 -0
- package/templates/base/tools/generator/templates/service/service.template.js.hbs +17 -0
- package/templates/base/tools/generator/templates/service/service.template.test.js.hbs +22 -0
- package/templates/base/tools/generator/templates/test/test.template.js.hbs +10 -0
- package/templates/base/tools/generator/templates/test/test.template.test.js.hbs +27 -0
- package/templates/base/tools/generator/templates/{view.template.js.hbs → view/view.template.js.hbs} +4 -3
- package/templates/base/tools/generator/templates/view/view.template.test.js.hbs +24 -0
- package/templates/base/tools/testin-nutin/core/coverage/collect-coverage.js.hbs +39 -0
- package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.js.hbs +115 -0
- package/templates/base/tools/testin-nutin/core/coverage/compute-coverage.test.js.hbs +74 -0
- package/templates/base/tools/testin-nutin/core/coverage/write-summary-report.js.hbs +54 -0
- package/templates/base/tools/testin-nutin/core/coverage/write-uncovered-report.js.hbs +44 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/assertion-lib.js.hbs +62 -24
- package/templates/base/tools/testin-nutin/core/globals/clock.js.hbs +206 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/jsdom-setup.js.hbs +19 -6
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/register-test-globals.js.hbs +13 -4
- package/templates/base/tools/testin-nutin/core/globals/silence-console.js.hbs +23 -0
- package/templates/{features/testinNutin/testin-nutin/core/src → base/tools/testin-nutin/core}/globals/spyon.js.hbs +2 -0
- package/templates/base/tools/testin-nutin/core/index.js.hbs +9 -0
- package/templates/base/tools/testin-nutin/core/printer.js.hbs +85 -0
- package/templates/base/tools/testin-nutin/core/queue/queue.test.js.hbs +46 -0
- package/templates/base/tools/testin-nutin/core/queue/test-discovery.test.js.hbs +46 -0
- package/templates/base/tools/testin-nutin/core/queue/test-queue.js.hbs +154 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/core/tests/assertions.test.js.hbs +27 -0
- package/templates/base/tools/testin-nutin/core/tests/clock.test.js.hbs +118 -0
- package/templates/base/tools/testin-nutin/core/tests/globals-each-all.test.js.hbs +28 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/create-mock-method.js.hbs +2 -1
- package/templates/base/tools/testin-nutin/mocks/create-mock-method.test.js.hbs +64 -0
- package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-event-bus.js.hbs +3 -2
- package/templates/base/tools/testin-nutin/mocks/mock-event-bus.test.js.hbs +66 -0
- package/templates/base/tools/testin-nutin/mocks/mock-http-client.test.js.hbs +26 -0
- package/templates/base/tools/testin-nutin/mocks/mock-i18n.test.js.hbs +61 -0
- package/templates/base/tools/testin-nutin/mocks/mock-router.test.js.hbs +29 -0
- package/templates/base/tools/testin-nutin/runner.js.hbs +100 -0
- package/templates/base/tools/testin-nutin/watch-tests.js.hbs +37 -0
- package/templates/base/tools/utils/error-exit.js.hbs +8 -0
- package/templates/base/tools/utils/format.js.hbs +10 -10
- package/templates/base/tools/utils/format.test.js.hbs +45 -0
- package/templates/base/tools/utils/get-files-recursive.js.hbs +6 -1
- package/templates/base/tools/utils/get-files-recursive.test.js.hbs +49 -0
- package/templates/base/tools/utils/get-rel-to-core.test.js.hbs +18 -0
- package/templates/base/tools/utils/index.js.hbs +1 -3
- package/templates/base/tools/utils/languages.js.hbs +15 -0
- package/templates/base/tools/utils/languages.test.js.hbs +16 -0
- package/templates/base/tools/utils/print.js.hbs +19 -13
- package/templates/base/tools/utils/print.test.js.hbs +60 -0
- package/templates/base/tools/utils/prompt-boolean.js.hbs +7 -0
- package/templates/base/tools/utils/prompt-boolean.test.js.hbs +54 -0
- package/templates/base/tools/utils/run-command.js.hbs +4 -2
- package/templates/base/tools/utils/run-command.test.js.hbs +19 -0
- package/templates/base/tools/utils/run-script.js.hbs +14 -3
- package/templates/base/tools/utils/run-script.test.js.hbs +40 -0
- package/templates/features/{deployHelper → docker}/.dockerignore.hbs +4 -4
- package/templates/features/docker/tools/docker/Dockerfile.template.hbs +51 -0
- package/templates/features/{deployHelper/tools/deployment/nginx.conf.hbs → docker/tools/docker/nginx.conf.template} +4 -2
- package/templates/features/docker/tools/docker/scripts/docker-build.js.hbs +20 -0
- package/templates/features/docker/tools/docker/scripts/docker-ports.js.hbs +48 -0
- package/templates/features/docker/tools/docker/scripts/docker-ports.test.js.hbs +52 -0
- package/templates/features/docker/tools/docker/scripts/docker-run.js.hbs +28 -0
- package/templates/features/docker/tools/docker/scripts/render-docker-template.js.hbs +23 -0
- package/templates/features/docker/tools/docker/scripts/render-docker-template.test.js.hbs +60 -0
- package/templates/features/docker/tools/docker/scripts/validate-docker.js.hbs +30 -0
- package/lib/src/context-builder.mjs +0 -40
- package/lib/src/file-generator.mjs +0 -109
- package/lib/src/json-generator.mjs +0 -31
- package/lib/src/package-manager.mjs +0 -139
- package/lib/src/print.mjs +0 -32
- package/lib/src/utils.mjs +0 -18
- package/lib/src/version.mjs +0 -1
- package/templates/base/builder.config.js.hbs +0 -36
- package/templates/base/src/app/components/common/anchor/anchor.component.ts.hbs +0 -32
- package/templates/base/src/app/components/common/button/button.component.ts.hbs +0 -40
- package/templates/base/src/app/dom.test.js.hbs +0 -13
- package/templates/base/src/app/services/example/example.service.ts.hbs +0 -18
- package/templates/base/src/app/views/home/home.view.test.js.hbs +0 -26
- package/templates/base/src/core/services/store/store.test.js.hbs +0 -8
- package/templates/base/src/core/services/store/store.ts.hbs +0 -39
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.test.js.hbs +0 -0
- package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +0 -113
- package/templates/base/src/core/utils/button-manager/button-manager.utils.test.js.hbs +0 -249
- package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +0 -131
- package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +0 -50
- package/templates/base/src/core/utils/index.ts.hbs +0 -2
- package/templates/base/src/libs/index.ts.hbs +0 -4
- package/templates/base/src/libs/popover/helpers/focus-trap.helper.ts.hbs +0 -197
- package/templates/base/src/libs/popover/helpers/popover-dom.helper.ts.hbs +0 -75
- package/templates/base/src/libs/popover/popover.ts.hbs +0 -138
- package/templates/base/src/libs/snackbar/snackbar.ts.hbs +0 -75
- package/templates/base/src/styles/base/_functions.scss.hbs +0 -9
- package/templates/base/src/styles/core/_anchors.scss.hbs +0 -10
- package/templates/base/src/styles/core/_index.scss.hbs +0 -6
- package/templates/base/src/styles/core/_popover.scss.hbs +0 -96
- package/templates/base/src/styles/core/_snackbar.scss.hbs +0 -50
- package/templates/base/tools/builder/core/esbuild.js.hbs +0 -25
- package/templates/base/tools/builder/core/minify-html.js.hbs +0 -60
- package/templates/base/tools/builder/core/sass.js.hbs +0 -22
- package/templates/base/tools/builder/core/tsc.js.hbs +0 -12
- package/templates/base/tools/generator/templates/service.template.js.hbs +0 -17
- package/templates/base/tools/utils/export-flags.js.hbs +0 -6
- package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +0 -56
- package/templates/features/deployHelper/tools/deployment/Dockerfile.hbs +0 -30
- package/templates/features/i18n/src/app/views/home/locales/en.json.hbs +0 -9
- package/templates/features/i18n/src/app/views/not-found/locales/en.json +0 -8
- package/templates/features/i18n/src/app/views/not-found/locales/fr.json +0 -8
- package/templates/features/i18n/src/core/config.ts.hbs +0 -3
- package/templates/features/i18n/src/core/services/i18n/i18n.test.js.hbs +0 -125
- package/templates/features/i18n/tools/generator/templates/json.template.js.hbs +0 -8
- package/templates/features/i18n/tools/utils/languages.js.hbs +0 -7
- package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +0 -9
- package/templates/features/stylinNutin/src/styles/base/_mixins.scss.hbs +0 -372
- package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +0 -3
- package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +0 -254
- package/templates/features/stylinNutin/src/styles/views/_index.scss.hbs +0 -0
- package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +0 -4
- package/templates/features/template/src/app/views/home/home.view.html.hbs +0 -56
- package/templates/features/template/src/app/views/not-found/not-found.view.html.hbs +0 -19
- package/templates/features/template/tools/builder/core/merge-templates.js.hbs +0 -49
- package/templates/features/template/tools/generator/templates/html.template.js.hbs +0 -2
- package/templates/features/testinNutin/testin-nutin/core/index.js.hbs +0 -5
- package/templates/features/testinNutin/testin-nutin/core/src/utils/print.js.hbs +0 -64
- package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +0 -64
- package/templates/features/testinNutin/testin-nutin/core/tests/globals-each-all.test.js.hbs +0 -28
- package/templates/features/testinNutin/testin-nutin/mocks/mock-store.js.hbs +0 -38
- package/templates/features/testinNutin/testin-nutin/runner.js.hbs +0 -36
- package/templates/features/testinNutin/testin-nutin/watch-tests.js.hbs +0 -21
- package/templates/features/testinNutin/testin-nutin.config.js.hbs +0 -14
- /package/lib/src/{template-compiler.mjs → common/template-compiler.mjs} +0 -0
- /package/templates/{features/i18n → base}/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +0 -0
- /package/templates/{features/i18n → base}/src/core/services/i18n/languages.ts.hbs +0 -0
- /package/templates/base/tools/builder/{variables → core/app}/binary-extensions.js.hbs +0 -0
- /package/templates/base/tools/builder/core/{paths.js.hbs → app/paths.js.hbs} +0 -0
- /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/queue.js.hbs +0 -0
- /package/templates/{features/testinNutin/testin-nutin/core/src/utils → base/tools/testin-nutin/core/queue}/test-discovery.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-http-client.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-i18n.js.hbs +0 -0
- /package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-router.js.hbs +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { print, chalk } from '../../utils/index.js';
|
|
2
|
+
import config from '#root/nutin.config.js';
|
|
3
|
+
|
|
4
|
+
export { chalk, print } from '../../utils/index.js';
|
|
5
|
+
|
|
6
|
+
const verboseEnabled = process.argv.includes('--verbose');
|
|
7
|
+
|
|
8
|
+
export function printSummary(todo, passed, failed, time) {
|
|
9
|
+
const totalTime = time.toFixed(2);
|
|
10
|
+
const total = passed + failed;
|
|
11
|
+
|
|
12
|
+
print.boldBlue(`Test Summary`);
|
|
13
|
+
print.boldInfo(`✓ ${passed} passed`);
|
|
14
|
+
print.boldError(`✗ ${failed} failed`);
|
|
15
|
+
print.info(`📖 ${total} total`);
|
|
16
|
+
print.info(`⌚️ Time: ${totalTime}ms\n`);
|
|
17
|
+
if (todo > 0) print.boldWarn(`⚠️ To do: ${todo}\n`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function printResults(results) {
|
|
21
|
+
const statusOrder = { passed: 0, todo: 1, failed: 2 };
|
|
22
|
+
const sortedResults = results.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
|
|
23
|
+
|
|
24
|
+
sortedResults.forEach(result => {
|
|
25
|
+
if (result.status === 'passed') {
|
|
26
|
+
if (verboseEnabled) console.log(`${chalk.green('✓')} ${chalk.cyan(`${result.suiteName}`)} - ${chalk.green(`${result.name}`)}`);
|
|
27
|
+
} else if (result.status === 'todo') {
|
|
28
|
+
print.warn(`TODO: ${result.name} in ${toTsPath(result.file)}`);
|
|
29
|
+
} else {
|
|
30
|
+
print.error(`✗ ${result.suiteName} - ${result.name}`);
|
|
31
|
+
print.grayError(result.error);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function printStart() {
|
|
37
|
+
print.boldHead('\nRunning tests...\n');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function printCoverageReport(report) {
|
|
41
|
+
if (!report.length) {
|
|
42
|
+
print.warn('No app source files were covered.');
|
|
43
|
+
return { globalLinesPct: 100, globalFunctionsPct: 100, globalBranchesPct: 100 };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
print.boldBlue('\nCoverage Summary');
|
|
47
|
+
console.table(
|
|
48
|
+
report
|
|
49
|
+
.sort((a, b) => a.file.localeCompare(b.file))
|
|
50
|
+
.map(r => ({
|
|
51
|
+
File: toTsPath(r.file),
|
|
52
|
+
'Lines %': r.linesPct.toFixed(1),
|
|
53
|
+
'Functions %': r.functionsPct.toFixed(1),
|
|
54
|
+
'Branches %': r.branchesPct.toFixed(1),
|
|
55
|
+
}))
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const totals = report.reduce((acc, r) => ({
|
|
59
|
+
coveredLines: acc.coveredLines + r.coveredLines,
|
|
60
|
+
totalLines: acc.totalLines + r.totalLines,
|
|
61
|
+
coveredBranches: acc.coveredBranches + r.coveredBranches,
|
|
62
|
+
totalBranches: acc.totalBranches + r.totalBranches,
|
|
63
|
+
coveredFunctions: acc.coveredFunctions + r.coveredFunctions,
|
|
64
|
+
totalFunctions: acc.totalFunctions + r.totalFunctions,
|
|
65
|
+
}), { coveredLines: 0, totalLines: 0, coveredBranches: 0, totalBranches: 0, coveredFunctions: 0, totalFunctions: 0 });
|
|
66
|
+
|
|
67
|
+
const globalLinesPct = totals.totalLines ? (totals.coveredLines / totals.totalLines) * 100 : 100;
|
|
68
|
+
const globalFunctionsPct = totals.totalFunctions ? (totals.coveredFunctions / totals.totalFunctions) * 100 : 100;
|
|
69
|
+
const globalBranchesPct = totals.totalBranches ? (totals.coveredBranches / totals.totalBranches) * 100 : 100;
|
|
70
|
+
|
|
71
|
+
const threshold = config.testinNutin.coverage?.threshold;
|
|
72
|
+
const thresholdSuffix = typeof threshold === 'number' ? ` (threshold: ${threshold}%)` : '';
|
|
73
|
+
print.boldInfo(`Global: ${globalBranchesPct.toFixed(1)}% branches, ${globalFunctionsPct.toFixed(1)}% functions, ${globalLinesPct.toFixed(1)}% lines${thresholdSuffix}\n`);
|
|
74
|
+
|
|
75
|
+
return { globalLinesPct, globalFunctionsPct, globalBranchesPct };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function toTsPath(input) {
|
|
79
|
+
return input
|
|
80
|
+
.split('/')
|
|
81
|
+
.slice(1)
|
|
82
|
+
.join('/')
|
|
83
|
+
.replace(/\.js$/, '.ts');
|
|
84
|
+
}
|
|
85
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Queue from './queue.js';
|
|
2
|
+
|
|
3
|
+
describe('Queue', () => {
|
|
4
|
+
it('starts empty', () => {
|
|
5
|
+
const queue = new Queue();
|
|
6
|
+
expect(queue.length).toBe(0);
|
|
7
|
+
expect(queue.deque()).toBe(undefined);
|
|
8
|
+
expect(queue.peek()).toBe(undefined);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('dequeues items in FIFO order', () => {
|
|
12
|
+
const queue = new Queue();
|
|
13
|
+
queue.enqueue('a');
|
|
14
|
+
queue.enqueue('b');
|
|
15
|
+
queue.enqueue('c');
|
|
16
|
+
|
|
17
|
+
expect(queue.length).toBe(3);
|
|
18
|
+
expect(queue.deque()).toBe('a');
|
|
19
|
+
expect(queue.deque()).toBe('b');
|
|
20
|
+
expect(queue.length).toBe(1);
|
|
21
|
+
expect(queue.deque()).toBe('c');
|
|
22
|
+
expect(queue.length).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('peek returns the next item without removing it', () => {
|
|
26
|
+
const queue = new Queue();
|
|
27
|
+
queue.enqueue('first');
|
|
28
|
+
queue.enqueue('second');
|
|
29
|
+
|
|
30
|
+
expect(queue.peek()).toBe('first');
|
|
31
|
+
expect(queue.peek()).toBe('first');
|
|
32
|
+
expect(queue.length).toBe(2);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('handles a single enqueue/deque cycle and can be reused afterward', () => {
|
|
36
|
+
const queue = new Queue();
|
|
37
|
+
queue.enqueue('only');
|
|
38
|
+
expect(queue.deque()).toBe('only');
|
|
39
|
+
expect(queue.length).toBe(0);
|
|
40
|
+
expect(queue.deque()).toBe(undefined);
|
|
41
|
+
|
|
42
|
+
queue.enqueue('again');
|
|
43
|
+
expect(queue.length).toBe(1);
|
|
44
|
+
expect(queue.deque()).toBe('again');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { getTestFiles } from './test-discovery.js';
|
|
6
|
+
|
|
7
|
+
// getTestFiles resolves its own `projectRoot` anchor one directory short of the real
|
|
8
|
+
// project root (see tools/testin-nutin/runner.js, which always calls it as
|
|
9
|
+
// `getTestFiles(path.join('..', origin))` to compensate). This test file lives in the
|
|
10
|
+
// same directory as test-discovery.js, so it can recompute that same anchor to build
|
|
11
|
+
// `dir` arguments the same way the real runner does.
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const relativeToCwd = path.relative(process.cwd(), __dirname);
|
|
14
|
+
const levelsUp = relativeToCwd.split(path.sep).length - 1;
|
|
15
|
+
const projectRoot = path.resolve(__dirname, ...Array(levelsUp).fill('..'));
|
|
16
|
+
|
|
17
|
+
describe('getTestFiles', () => {
|
|
18
|
+
let tmpDir;
|
|
19
|
+
let relDir;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nutin-test-discovery-'));
|
|
23
|
+
relDir = path.relative(projectRoot, tmpDir);
|
|
24
|
+
|
|
25
|
+
fs.mkdirSync(path.join(tmpDir, 'nested'), { recursive: true });
|
|
26
|
+
fs.writeFileSync(path.join(tmpDir, 'a.test.js'), '');
|
|
27
|
+
fs.writeFileSync(path.join(tmpDir, 'b.js'), '');
|
|
28
|
+
fs.writeFileSync(path.join(tmpDir, 'nested', 'c.test.js'), '');
|
|
29
|
+
fs.writeFileSync(path.join(tmpDir, 'nested', 'readme.md'), '');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('recursively finds only files ending in .test.js', () => {
|
|
37
|
+
const files = getTestFiles(relDir).sort();
|
|
38
|
+
expect(files).toEqual(
|
|
39
|
+
[path.join(tmpDir, 'a.test.js'), path.join(tmpDir, 'nested', 'c.test.js')].sort()
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns an empty array for a directory that does not exist', () => {
|
|
44
|
+
expect(getTestFiles(path.join(relDir, 'does-not-exist'))).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import Queue from './queue.js';
|
|
3
|
+
import { printStart, printSummary, printResults } from '../index.js';
|
|
4
|
+
import { setupJsdom, teardownJsdom } from '../globals/jsdom-setup.js';
|
|
5
|
+
|
|
6
|
+
const testQueue = new Queue();
|
|
7
|
+
let todo = 0;
|
|
8
|
+
let passed = 0;
|
|
9
|
+
let failed = 0;
|
|
10
|
+
let elapsedTime;
|
|
11
|
+
let currentTestFile = '';
|
|
12
|
+
|
|
13
|
+
export function setCurrentTestFile(file) {
|
|
14
|
+
currentTestFile = file ? path.relative(process.cwd(), file) : '';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function addTest(test) {
|
|
18
|
+
testQueue.enqueue({ ...test, file: currentTestFile });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function runQueuedTests() {
|
|
22
|
+
printStart();
|
|
23
|
+
const startTime = performance.now();
|
|
24
|
+
const testAmount = testQueue.length;
|
|
25
|
+
const results = [];
|
|
26
|
+
let previousSuiteKey = null;
|
|
27
|
+
let previousSuite = null;
|
|
28
|
+
let previousSuiteAfterAll = null;
|
|
29
|
+
let test;
|
|
30
|
+
|
|
31
|
+
const runAfterAll = async (suiteName, afterAllFn) => {
|
|
32
|
+
if (!afterAllFn) return;
|
|
33
|
+
try {
|
|
34
|
+
await afterAllFn();
|
|
35
|
+
} catch (err) {
|
|
36
|
+
failed++;
|
|
37
|
+
results.push({
|
|
38
|
+
suiteName,
|
|
39
|
+
status: 'failed',
|
|
40
|
+
name: 'afterAll',
|
|
41
|
+
error: err.stack
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const suiteKey = (t) => `${t.file}::${t.suiteName}`;
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < testAmount; i++) {
|
|
49
|
+
test = testQueue.deque();
|
|
50
|
+
|
|
51
|
+
if (suiteKey(test) !== previousSuiteKey) {
|
|
52
|
+
await runAfterAll(previousSuite, previousSuiteAfterAll);
|
|
53
|
+
if (previousSuiteKey !== null) teardownJsdom();
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
setupJsdom();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
failed++;
|
|
59
|
+
results.push({
|
|
60
|
+
suiteName: test.suiteName,
|
|
61
|
+
status: 'failed',
|
|
62
|
+
name: 'setupJsdom',
|
|
63
|
+
error: err.stack
|
|
64
|
+
});
|
|
65
|
+
previousSuiteKey = suiteKey(test);
|
|
66
|
+
previousSuite = test.suiteName;
|
|
67
|
+
previousSuiteAfterAll = null;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
previousSuiteKey = suiteKey(test);
|
|
72
|
+
previousSuite = test.suiteName;
|
|
73
|
+
previousSuiteAfterAll = test.afterAll;
|
|
74
|
+
if (test.beforeAll) {
|
|
75
|
+
try {
|
|
76
|
+
await test.beforeAll();
|
|
77
|
+
} catch (err) {
|
|
78
|
+
failed++;
|
|
79
|
+
results.push({
|
|
80
|
+
suiteName: test.suiteName,
|
|
81
|
+
status: 'failed',
|
|
82
|
+
name: 'beforeAll',
|
|
83
|
+
error: err.stack
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (test.isTodo) {
|
|
90
|
+
todo++;
|
|
91
|
+
results.push({
|
|
92
|
+
suiteName: test.suiteName,
|
|
93
|
+
status: 'todo',
|
|
94
|
+
name: test.testName,
|
|
95
|
+
file: test.file
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let testError = null;
|
|
101
|
+
try {
|
|
102
|
+
if (test.beforeEach) await test.beforeEach();
|
|
103
|
+
await test.testFn();
|
|
104
|
+
} catch (err) {
|
|
105
|
+
testError = err;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let afterEachError = null;
|
|
109
|
+
try {
|
|
110
|
+
if (test.afterEach) await test.afterEach();
|
|
111
|
+
} catch (err) {
|
|
112
|
+
afterEachError = err;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (testError) {
|
|
116
|
+
failed++;
|
|
117
|
+
results.push({
|
|
118
|
+
suiteName: test.suiteName,
|
|
119
|
+
status: 'failed',
|
|
120
|
+
name: test.testName,
|
|
121
|
+
error: testError.stack
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
passed++;
|
|
125
|
+
results.push({
|
|
126
|
+
suiteName: test.suiteName,
|
|
127
|
+
status: 'passed',
|
|
128
|
+
name: test.testName
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (afterEachError) {
|
|
133
|
+
failed++;
|
|
134
|
+
results.push({
|
|
135
|
+
suiteName: test.suiteName,
|
|
136
|
+
status: 'failed',
|
|
137
|
+
name: `${test.testName} (afterEach)`,
|
|
138
|
+
error: afterEachError.stack
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (previousSuiteKey !== null) {
|
|
144
|
+
await runAfterAll(previousSuite, previousSuiteAfterAll);
|
|
145
|
+
teardownJsdom();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const endTime = performance.now();
|
|
149
|
+
elapsedTime = (endTime - startTime);
|
|
150
|
+
printResults(results);
|
|
151
|
+
printSummary(todo, passed, failed, elapsedTime);
|
|
152
|
+
|
|
153
|
+
return { todo, passed, failed, elapsedTime };
|
|
154
|
+
}
|
package/templates/{features/testinNutin → base/tools}/testin-nutin/core/tests/assertions.test.js.hbs
RENAMED
|
@@ -67,4 +67,31 @@ describe('assertions', () => {
|
|
|
67
67
|
expect(notErr).not.toThrow();
|
|
68
68
|
expect(notErr).not.toThrow('messsage');
|
|
69
69
|
});
|
|
70
|
+
it('should support toThrow with an async function that rejects', async () => {
|
|
71
|
+
const rejects = async () => { throw new Error('async failure'); };
|
|
72
|
+
const resolves = async () => 'ok';
|
|
73
|
+
await expect(rejects).toThrow();
|
|
74
|
+
await expect(rejects).toThrow('async failure');
|
|
75
|
+
await expect(resolves).not.toThrow();
|
|
76
|
+
});
|
|
77
|
+
it('should throw a distinguishable AssertionError on failure', () => {
|
|
78
|
+
let caught;
|
|
79
|
+
try {
|
|
80
|
+
expect(1).toBe(2);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
caught = err;
|
|
83
|
+
}
|
|
84
|
+
expect(caught).toBeInstanceOf(AssertionError);
|
|
85
|
+
});
|
|
86
|
+
it('.not should not swallow an unrelated runtime error from the matcher itself', () => {
|
|
87
|
+
// `x instanceof undefined` throws a TypeError — a bug in the test, not a
|
|
88
|
+
// legitimate assertion mismatch, so it must not be reported as a pass.
|
|
89
|
+
let caught;
|
|
90
|
+
try {
|
|
91
|
+
expect({}).not.toBeInstanceOf(undefined);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
caught = err;
|
|
94
|
+
}
|
|
95
|
+
expect(caught).toBeInstanceOf(TypeError);
|
|
96
|
+
});
|
|
70
97
|
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
describe('clock (fake timers)', () => {
|
|
2
|
+
afterEach(() => {
|
|
3
|
+
useRealTimers();
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
it('should fake setTimeout and fire it on advanceTimersByTime', () => {
|
|
7
|
+
useFakeTimers();
|
|
8
|
+
let called = false;
|
|
9
|
+
setTimeout(() => { called = true; }, 1000);
|
|
10
|
+
|
|
11
|
+
expect(called).toBeFalsy();
|
|
12
|
+
advanceTimersByTime(999);
|
|
13
|
+
expect(called).toBeFalsy();
|
|
14
|
+
advanceTimersByTime(1);
|
|
15
|
+
expect(called).toBeTruthy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should fire multiple timers in deadline order', () => {
|
|
19
|
+
useFakeTimers();
|
|
20
|
+
const order = [];
|
|
21
|
+
setTimeout(() => order.push('b'), 200);
|
|
22
|
+
setTimeout(() => order.push('a'), 100);
|
|
23
|
+
setTimeout(() => order.push('c'), 300);
|
|
24
|
+
|
|
25
|
+
advanceTimersByTime(300);
|
|
26
|
+
expect(order).toEqual(['a', 'b', 'c']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should fire setInterval repeatedly on a single advance', () => {
|
|
30
|
+
useFakeTimers();
|
|
31
|
+
let count = 0;
|
|
32
|
+
const id = setInterval(() => { count++; }, 10);
|
|
33
|
+
|
|
34
|
+
advanceTimersByTime(105);
|
|
35
|
+
expect(count).toEqual(10);
|
|
36
|
+
clearInterval(id);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should chase timers scheduled from within a firing timer via runAllTimers', () => {
|
|
40
|
+
useFakeTimers();
|
|
41
|
+
const order = [];
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
order.push('first');
|
|
44
|
+
setTimeout(() => order.push('nested'), 10);
|
|
45
|
+
}, 10);
|
|
46
|
+
|
|
47
|
+
runAllTimers();
|
|
48
|
+
expect(order).toEqual(['first', 'nested']);
|
|
49
|
+
expect(getTimerCount()).toEqual(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should bail out of an infinite setInterval loop in runAllTimers', () => {
|
|
53
|
+
useFakeTimers();
|
|
54
|
+
setInterval(() => {}, 0);
|
|
55
|
+
const throwing = () => runAllTimers();
|
|
56
|
+
|
|
57
|
+
expect(throwing).toThrow();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should only fire snapshot-pending timers with runOnlyPendingTimers', () => {
|
|
61
|
+
useFakeTimers();
|
|
62
|
+
let outer = 0;
|
|
63
|
+
let inner = 0;
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
outer++;
|
|
66
|
+
setTimeout(() => { inner++; }, 0);
|
|
67
|
+
}, 0);
|
|
68
|
+
|
|
69
|
+
runOnlyPendingTimers();
|
|
70
|
+
expect(outer).toEqual(1);
|
|
71
|
+
expect(inner).toEqual(0);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should clear all pending timers without firing them', () => {
|
|
75
|
+
useFakeTimers();
|
|
76
|
+
let called = false;
|
|
77
|
+
setTimeout(() => { called = true; }, 10);
|
|
78
|
+
|
|
79
|
+
expect(getTimerCount()).toEqual(1);
|
|
80
|
+
clearAllTimers();
|
|
81
|
+
expect(getTimerCount()).toEqual(0);
|
|
82
|
+
advanceTimersByTime(1000);
|
|
83
|
+
expect(called).toBeFalsy();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should reflect fake time in Date.now() and new Date()', () => {
|
|
87
|
+
useFakeTimers();
|
|
88
|
+
setSystemTime(1000000);
|
|
89
|
+
|
|
90
|
+
expect(Date.now()).toEqual(1000000);
|
|
91
|
+
expect(new Date().getTime()).toEqual(1000000);
|
|
92
|
+
advanceTimersByTime(500);
|
|
93
|
+
expect(Date.now()).toEqual(1000500);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should still construct explicit Date args normally under fake time', () => {
|
|
97
|
+
useFakeTimers();
|
|
98
|
+
setSystemTime(1000000);
|
|
99
|
+
const explicit = new Date(2020, 0, 1);
|
|
100
|
+
|
|
101
|
+
expect(explicit.getFullYear()).toEqual(2020);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should restore real timers after useRealTimers()', async () => {
|
|
105
|
+
useFakeTimers();
|
|
106
|
+
useRealTimers();
|
|
107
|
+
|
|
108
|
+
await new Promise(resolve => {
|
|
109
|
+
setTimeout(resolve, 5);
|
|
110
|
+
});
|
|
111
|
+
expect(true).toBeTruthy();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should throw a clear error when used without useFakeTimers()', () => {
|
|
115
|
+
const throwing = () => advanceTimersByTime(100);
|
|
116
|
+
expect(throwing).toThrow('useFakeTimers');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
describe('globals beforeEach/All and afterEach/All', () => {
|
|
2
|
+
let beforeEachCount = 0;
|
|
3
|
+
let beforeAllCount = 0;
|
|
4
|
+
let afterEachCount = 0;
|
|
5
|
+
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
beforeAllCount++;
|
|
8
|
+
});
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
beforeEachCount++;
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
afterEachCount++;
|
|
14
|
+
});
|
|
15
|
+
afterAll(() => {
|
|
16
|
+
// console.debug('afterAll called.');
|
|
17
|
+
});
|
|
18
|
+
it('should be called once for all and once for each', () => {
|
|
19
|
+
expect(beforeAllCount).toEqual(1);
|
|
20
|
+
expect(beforeEachCount).toEqual(1);
|
|
21
|
+
expect(afterEachCount).toEqual(0);
|
|
22
|
+
});
|
|
23
|
+
it('should be called once for all and twice for each', () => {
|
|
24
|
+
expect(beforeAllCount).toEqual(1);
|
|
25
|
+
expect(beforeEachCount).toEqual(2);
|
|
26
|
+
expect(afterEachCount).toEqual(1);
|
|
27
|
+
});
|
|
28
|
+
})
|
package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/create-mock-method.js.hbs
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export function createMockMethod() {
|
|
1
|
+
export function createMockMethod(defaultImpl) {
|
|
2
2
|
const calls = [];
|
|
3
3
|
const fn = (...args) => {
|
|
4
4
|
calls.push(args);
|
|
5
5
|
if (fn.__mockReturn !== undefined) return fn.__mockReturn;
|
|
6
6
|
if (fn.__mockReturnFn) return fn.__mockReturnFn(...args);
|
|
7
|
+
if (defaultImpl) return defaultImpl(...args);
|
|
7
8
|
};
|
|
8
9
|
fn.calls = calls;
|
|
9
10
|
fn.mockReturnValue = (value) => { fn.__mockReturn = value; };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createMockMethod } from './create-mock-method.js';
|
|
2
|
+
|
|
3
|
+
describe('createMockMethod', () => {
|
|
4
|
+
it('records every call', () => {
|
|
5
|
+
const fn = createMockMethod();
|
|
6
|
+
fn(1, 2);
|
|
7
|
+
fn('a');
|
|
8
|
+
|
|
9
|
+
expect(fn.calls).toEqual([[1, 2], ['a']]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('returns undefined by default', () => {
|
|
13
|
+
const fn = createMockMethod();
|
|
14
|
+
expect(fn()).toBe(undefined);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('mockReturnValue makes every call return a fixed value', () => {
|
|
18
|
+
const fn = createMockMethod();
|
|
19
|
+
fn.mockReturnValue(42);
|
|
20
|
+
|
|
21
|
+
expect(fn()).toBe(42);
|
|
22
|
+
expect(fn('ignored arg')).toBe(42);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('mockImplementation delegates to the given function', () => {
|
|
26
|
+
const fn = createMockMethod();
|
|
27
|
+
fn.mockImplementation((a, b) => a + b);
|
|
28
|
+
|
|
29
|
+
expect(fn(2, 3)).toBe(5);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('mockReset clears recorded calls and configured behavior', () => {
|
|
33
|
+
const fn = createMockMethod();
|
|
34
|
+
fn.mockReturnValue(42);
|
|
35
|
+
fn(1);
|
|
36
|
+
|
|
37
|
+
fn.mockReset();
|
|
38
|
+
|
|
39
|
+
expect(fn.calls).toEqual([]);
|
|
40
|
+
expect(fn()).toBe(undefined);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('falls back to the given default implementation', () => {
|
|
44
|
+
const fn = createMockMethod((a, b) => a + b);
|
|
45
|
+
|
|
46
|
+
expect(fn(2, 3)).toBe(5);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('mockImplementation overrides the default implementation', () => {
|
|
50
|
+
const fn = createMockMethod(() => 'default');
|
|
51
|
+
fn.mockImplementation(() => 'overridden');
|
|
52
|
+
|
|
53
|
+
expect(fn()).toBe('overridden');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('mockReset restores the default implementation', () => {
|
|
57
|
+
const fn = createMockMethod(() => 'default');
|
|
58
|
+
fn.mockImplementation(() => 'overridden');
|
|
59
|
+
|
|
60
|
+
fn.mockReset();
|
|
61
|
+
|
|
62
|
+
expect(fn()).toBe('default');
|
|
63
|
+
});
|
|
64
|
+
});
|
package/templates/{features/testinNutin → base/tools}/testin-nutin/mocks/mock-event-bus.js.hbs
RENAMED
|
@@ -24,10 +24,10 @@ export class MockEventBus {
|
|
|
24
24
|
this.off.calls = [];
|
|
25
25
|
this.off.mockReset = () => { this.off.calls = []; };
|
|
26
26
|
|
|
27
|
-
this.on = (event, callback) => {
|
|
27
|
+
this.on = createMockMethod((event, callback) => {
|
|
28
28
|
if (!this.handlers[event]) this.handlers[event] = [];
|
|
29
29
|
this.handlers[event].push(callback);
|
|
30
|
-
};
|
|
30
|
+
});
|
|
31
31
|
|
|
32
32
|
this.onDestroy = createMockMethod();
|
|
33
33
|
this.reset = this.reset.bind(this);
|
|
@@ -37,6 +37,7 @@ export class MockEventBus {
|
|
|
37
37
|
this.subscribe.mockReset();
|
|
38
38
|
this.emit.mockReset();
|
|
39
39
|
this.off.mockReset();
|
|
40
|
+
this.on.mockReset();
|
|
40
41
|
this.onDestroy.mockReset();
|
|
41
42
|
this.handlers = {};
|
|
42
43
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { MockEventBus } from './mock-event-bus.js';
|
|
2
|
+
|
|
3
|
+
describe('MockEventBus', () => {
|
|
4
|
+
it('on() + emit() dispatches the event data to the registered handler', () => {
|
|
5
|
+
const bus = new MockEventBus();
|
|
6
|
+
let received;
|
|
7
|
+
bus.on('greet', (data) => { received = data; });
|
|
8
|
+
|
|
9
|
+
bus.emit('greet', 'hello');
|
|
10
|
+
|
|
11
|
+
expect(received).toBe('hello');
|
|
12
|
+
expect(bus.emit.calls).toEqual([['greet', 'hello']]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('on() is a trackable mock', () => {
|
|
16
|
+
const bus = new MockEventBus();
|
|
17
|
+
const handler = () => {};
|
|
18
|
+
bus.on('greet', handler);
|
|
19
|
+
|
|
20
|
+
expect(bus.on).toHaveBeenCalled();
|
|
21
|
+
expect(bus.on).toHaveBeenCalledWith('greet', handler);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('off() removes a previously registered handler', () => {
|
|
25
|
+
const bus = new MockEventBus();
|
|
26
|
+
let callCount = 0;
|
|
27
|
+
const handler = () => { callCount++; };
|
|
28
|
+
bus.on('greet', handler);
|
|
29
|
+
bus.off('greet', handler);
|
|
30
|
+
|
|
31
|
+
bus.emit('greet', 'hello');
|
|
32
|
+
|
|
33
|
+
expect(callCount).toBe(0);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('subscribe() is a bare mock and does not actually register a handler', () => {
|
|
37
|
+
const bus = new MockEventBus();
|
|
38
|
+
let called = false;
|
|
39
|
+
const handler = () => { called = true; };
|
|
40
|
+
bus.subscribe('greet', handler);
|
|
41
|
+
|
|
42
|
+
bus.emit('greet', 'hello');
|
|
43
|
+
|
|
44
|
+
expect(called).toBeFalsy();
|
|
45
|
+
expect(bus.subscribe.calls.length).toBe(1);
|
|
46
|
+
expect(bus.subscribe.calls[0][0]).toBe('greet');
|
|
47
|
+
expect(bus.subscribe.calls[0][1]).toBe(handler);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('reset() clears handlers and call logs', () => {
|
|
51
|
+
const bus = new MockEventBus();
|
|
52
|
+
let callCount = 0;
|
|
53
|
+
bus.on('greet', () => { callCount++; });
|
|
54
|
+
bus.emit('greet', 'x');
|
|
55
|
+
bus.subscribe('greet', () => {});
|
|
56
|
+
|
|
57
|
+
bus.reset();
|
|
58
|
+
|
|
59
|
+
expect(bus.emit.calls).toEqual([]);
|
|
60
|
+
expect(bus.subscribe.calls).toEqual([]);
|
|
61
|
+
expect(bus.on.calls).toEqual([]);
|
|
62
|
+
|
|
63
|
+
bus.emit('greet', 'y');
|
|
64
|
+
expect(callCount).toBe(1); // only the pre-reset emit reached the handler
|
|
65
|
+
});
|
|
66
|
+
});
|