@nutin/cli 1.3.0 → 2.0.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 +122 -8
- package/README.md +21 -37
- 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 +81 -30
- 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/create-app/new-app-prompt.mjs +51 -0
- package/lib/src/{project-generator.mjs → create-app/project-generator.mjs} +15 -13
- package/lib/src/create-app/success-message.mjs +14 -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 +136 -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 +169 -0
- package/templates/base/GETTING_STARTED.md.hbs +227 -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 +51 -0
- package/templates/base/src/app/components/index.ts.hbs +1 -2
- package/templates/base/src/app/globals.d.ts.hbs +3 -15
- package/templates/base/src/{libs/guards → app}/guards.ts.hbs +1 -5
- package/templates/base/src/app/main.ts.hbs +7 -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 +1 -56
- 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 +4 -37
- 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 +81 -31
- 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 +38 -23
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.test.js.hbs +74 -0
- package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +1 -1
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.test.js.hbs +145 -0
- package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +41 -1
- 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 +13 -14
- 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 +82 -0
- 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 +27 -40
- 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/config.helper.ts.hbs +17 -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 +74 -0
- package/templates/base/src/core/base-classes/view/view.ts.hbs +17 -41
- package/templates/base/src/core/config.ts.hbs +7 -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 +149 -0
- package/templates/base/src/core/services/globals/globals.ts.hbs +95 -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 +43 -0
- 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 +60 -9
- 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 +149 -6
- package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +42 -30
- 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 +13 -70
- package/templates/base/src/core/services/router/router.test.js.hbs +226 -2
- package/templates/base/src/core/services/router/router.ts.hbs +40 -9
- 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 -85
- 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 +64 -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 +4 -0
- package/templates/base/tools/generator/templates/locale/locale.template.test.js.hbs +7 -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} +3 -2
- 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 -35
- 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 -31
- package/lib/src/prompts.mjs +0 -123
- package/lib/src/utils.mjs +0 -18
- package/lib/src/version.mjs +0 -1
- package/templates/base/builder.config.js.hbs +0 -35
- 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 -85
- 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 -108
- 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/popover.ts.hbs +0 -145
- package/templates/base/src/libs/snackbar/snackbar.ts.hbs +0 -72
- package/templates/base/src/styles/base/_functions.scss.hbs +0 -9
- package/templates/base/src/styles/core/_libs.scss.hbs +0 -132
- 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 -240
- 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,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
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { MockHttpClient } from './mock-http-client.js';
|
|
2
|
+
|
|
3
|
+
describe('MockHttpClient', () => {
|
|
4
|
+
it('exposes get/post/put/patch/delete as independent bare mocks', () => {
|
|
5
|
+
const client = new MockHttpClient();
|
|
6
|
+
client.get.mockReturnValue('get-result');
|
|
7
|
+
client.post('/widgets', { name: 'a' });
|
|
8
|
+
|
|
9
|
+
expect(client.get()).toBe('get-result');
|
|
10
|
+
expect(client.post.calls).toEqual([['/widgets', { name: 'a' }]]);
|
|
11
|
+
expect(client.put.calls).toEqual([]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('reset() clears calls and configured return values on every method', () => {
|
|
15
|
+
const client = new MockHttpClient();
|
|
16
|
+
client.get.mockReturnValue('cached');
|
|
17
|
+
client.get();
|
|
18
|
+
client.delete('/widgets/1');
|
|
19
|
+
|
|
20
|
+
client.reset();
|
|
21
|
+
|
|
22
|
+
expect(client.get.calls).toEqual([]);
|
|
23
|
+
expect(client.delete.calls).toEqual([]);
|
|
24
|
+
expect(client.get()).toBe(undefined);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { MockI18n } from './mock-i18n.js';
|
|
2
|
+
|
|
3
|
+
describe('MockI18n', () => {
|
|
4
|
+
it('exposes the constructor defaults via currentLanguage', () => {
|
|
5
|
+
const i18n = new MockI18n('fr', ['en', 'fr']);
|
|
6
|
+
expect(i18n.currentLanguage).toBe('fr');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('translate/getBrowserLanguage run their built-in default implementation without needing .mockImplementation', () => {
|
|
10
|
+
const i18n = new MockI18n();
|
|
11
|
+
|
|
12
|
+
// No translations set — translate() falls back to returning the key itself.
|
|
13
|
+
expect(i18n.translate('some.key')).toBe('some.key');
|
|
14
|
+
expect(i18n.getBrowserLanguage()).toBe('en');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('loadTranslations/initTranslations run their default implementation and update currentLanguage', async () => {
|
|
18
|
+
const i18n = new MockI18n('en', ['en', 'fr']);
|
|
19
|
+
|
|
20
|
+
await i18n.loadTranslations('fr');
|
|
21
|
+
expect(i18n.currentLanguage).toBe('fr');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('translate is still trackable as a mock alongside its default implementation', () => {
|
|
25
|
+
const i18n = new MockI18n();
|
|
26
|
+
i18n.translate('some.key');
|
|
27
|
+
|
|
28
|
+
expect(i18n.translate).toHaveBeenCalledWith('some.key');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('translate can be given real interpolation behavior via .mockImplementation', () => {
|
|
32
|
+
const i18n = new MockI18n();
|
|
33
|
+
i18n.setTranslations({ greeting: 'Hello {name}' });
|
|
34
|
+
i18n.translate.mockImplementation((key, params) => {
|
|
35
|
+
const value = i18n._translations[key] || key;
|
|
36
|
+
if (!params) return value;
|
|
37
|
+
return Object.entries(params).reduce((acc, [k, v]) => acc.replace(`{${k}}`, v), value);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(i18n.translate('greeting', { name: 'Ada' })).toBe('Hello Ada');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('setTranslations/setDefaultTranslations seed real, readable state', () => {
|
|
44
|
+
const i18n = new MockI18n();
|
|
45
|
+
i18n.setTranslations({ a: '1' });
|
|
46
|
+
i18n.setDefaultTranslations({ b: '2' });
|
|
47
|
+
|
|
48
|
+
expect(i18n._translations).toEqual({ a: '1' });
|
|
49
|
+
expect(i18n._defaultTranslations).toEqual({ b: '2' });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('reset() restores the constructor default language and clears translations', () => {
|
|
53
|
+
const i18n = new MockI18n('en');
|
|
54
|
+
i18n.setTranslations({ a: '1' });
|
|
55
|
+
|
|
56
|
+
i18n.reset();
|
|
57
|
+
|
|
58
|
+
expect(i18n._translations).toEqual({});
|
|
59
|
+
expect(i18n.currentLanguage).toBe('en');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MockRouter } from './mock-router.js';
|
|
2
|
+
|
|
3
|
+
describe('MockRouter', () => {
|
|
4
|
+
it('stores the routes passed to the constructor', () => {
|
|
5
|
+
const routes = { '/home': {} };
|
|
6
|
+
const router = new MockRouter(routes);
|
|
7
|
+
|
|
8
|
+
expect(router.routes).toBe(routes);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('navigate/handlePopState/handleNotFound/handleGuards/initializeEventListeners are independent bare mocks', () => {
|
|
12
|
+
const router = new MockRouter();
|
|
13
|
+
router.navigate('/about');
|
|
14
|
+
|
|
15
|
+
expect(router.navigate.calls).toEqual([['/about']]);
|
|
16
|
+
expect(router.handlePopState.calls).toEqual([]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('reset() clears every mock method and _currentView', () => {
|
|
20
|
+
const router = new MockRouter();
|
|
21
|
+
router.navigate('/about');
|
|
22
|
+
router._currentView = 'about-view';
|
|
23
|
+
|
|
24
|
+
router.reset();
|
|
25
|
+
|
|
26
|
+
expect(router.navigate.calls).toEqual([]);
|
|
27
|
+
expect(router._currentView).toBe(null);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import {
|
|
3
|
+
registerTestGlobals, getTestFiles, runQueuedTests, setCurrentTestFile, print,
|
|
4
|
+
startCoverage, stopCoverage, buildCoverageReport, printCoverageReport, maybeWriteUncoveredReport,
|
|
5
|
+
writeSummaryReport, setupJsdom, teardownJsdom,
|
|
6
|
+
} from './core/index.js';
|
|
7
|
+
import config from '#root/nutin.config.js';
|
|
8
|
+
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const coverageEnabled = args.includes('--coverage') || Boolean(config.testinNutin.coverage?.enabled);
|
|
11
|
+
const verboseEnabled = args.includes('--verbose');
|
|
12
|
+
const fileFilters = args.filter(arg => arg !== '--coverage' && arg !== '--verbose');
|
|
13
|
+
|
|
14
|
+
// Tests import and run the compiled build output (dist/src/)
|
|
15
|
+
// and has no sourcemap back to the .ts sources
|
|
16
|
+
const origins = [];
|
|
17
|
+
const coverageDirs = [];
|
|
18
|
+
|
|
19
|
+
if (config.testinNutin.includeFramework) {
|
|
20
|
+
const frameworkDir = path.join('src', 'core');
|
|
21
|
+
origins.push(frameworkDir);
|
|
22
|
+
if (coverageEnabled) coverageDirs.push(path.join('dist', frameworkDir));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (config.testinNutin.includeApp) {
|
|
26
|
+
const appDir = path.join('src', 'app');
|
|
27
|
+
origins.push(appDir);
|
|
28
|
+
if (coverageEnabled) coverageDirs.push(path.join('dist', appDir));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (config.testinNutin.includeTools) origins.push('tools');
|
|
32
|
+
|
|
33
|
+
registerTestGlobals();
|
|
34
|
+
|
|
35
|
+
let testFiles = origins.flatMap(
|
|
36
|
+
origin => getTestFiles(path.join('..', origin))
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (fileFilters.length) {
|
|
40
|
+
testFiles = testFiles.filter(file => fileFilters.some(f => file.includes(f)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function loadTestFile(file) {
|
|
44
|
+
setupJsdom();
|
|
45
|
+
setCurrentTestFile(file);
|
|
46
|
+
await import(file);
|
|
47
|
+
teardownJsdom();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function runCoverage(testResults) {
|
|
51
|
+
let scopedResults = await stopCoverage(coverageDirs);
|
|
52
|
+
if (fileFilters.length) {
|
|
53
|
+
scopedResults = scopedResults.filter(({ filePath }) => fileFilters.some(f => filePath.includes(f)));
|
|
54
|
+
}
|
|
55
|
+
const report = buildCoverageReport(scopedResults);
|
|
56
|
+
const globals = printCoverageReport(report);
|
|
57
|
+
writeSummaryReport(report, globals, testResults);
|
|
58
|
+
await maybeWriteUncoveredReport(report);
|
|
59
|
+
|
|
60
|
+
const threshold = config.testinNutin.coverage?.threshold;
|
|
61
|
+
if (typeof threshold === 'number') {
|
|
62
|
+
const misses = Object.entries({
|
|
63
|
+
lines: globals.globalLinesPct,
|
|
64
|
+
functions: globals.globalFunctionsPct,
|
|
65
|
+
branches: globals.globalBranchesPct,
|
|
66
|
+
}).filter(([, pct]) => pct < threshold);
|
|
67
|
+
|
|
68
|
+
if (misses.length) {
|
|
69
|
+
print.boldError(`\n✗ Coverage below threshold (${threshold}%): ${misses.map(([k, pct]) => `${k} ${pct.toFixed(1)}%`).join(', ')}\n`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function runTests() {
|
|
76
|
+
if (verboseEnabled) print.head('Test suites :');
|
|
77
|
+
|
|
78
|
+
if (coverageEnabled) await startCoverage();
|
|
79
|
+
|
|
80
|
+
for (const file of testFiles) {
|
|
81
|
+
try {
|
|
82
|
+
await loadTestFile(file);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
print.boldError(`✗ ${file} failed to load:`);
|
|
85
|
+
print.grayError(error.stack);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const testResults = await runQueuedTests();
|
|
90
|
+
|
|
91
|
+
if (testResults.failed > 0) process.exitCode = 1;
|
|
92
|
+
|
|
93
|
+
if (coverageEnabled) await runCoverage(testResults);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
runTests().catch((err) => {
|
|
97
|
+
print.boldError(`Unexpected error: ${err.message}`);
|
|
98
|
+
print.grayError(err.stack);
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { print } from './core/index.js'
|
|
5
|
+
|
|
6
|
+
const watcher = chokidar.watch(['src', 'test', 'unit', 'e2e'], {
|
|
7
|
+
ignored: /(^|[/\\])\../,
|
|
8
|
+
persistent: true,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
print.boldHead('Watching for test changes...')
|
|
12
|
+
|
|
13
|
+
watcher.on('error', (err) => {
|
|
14
|
+
print.boldError(`\nWatcher error: ${err.message}`);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
let isRunning = false;
|
|
18
|
+
let runTimeout = null;
|
|
19
|
+
|
|
20
|
+
watcher.on('change', (filePath) => {
|
|
21
|
+
if (runTimeout) clearTimeout(runTimeout);
|
|
22
|
+
|
|
23
|
+
runTimeout = setTimeout(() => {
|
|
24
|
+
if (isRunning) return;
|
|
25
|
+
isRunning = true;
|
|
26
|
+
|
|
27
|
+
console.clear();
|
|
28
|
+
print.boldInfo(`\n🔄 File changed: ${path.relative(process.cwd(), filePath)}\n`);
|
|
29
|
+
|
|
30
|
+
exec('node --experimental-modules testin-nutin/runner.js', { maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
31
|
+
if (stdout) process.stdout.write(stdout);
|
|
32
|
+
if (stderr) process.stderr.write(stderr);
|
|
33
|
+
if (err) print.boldError(`\nTest run failed: ${err.message}`);
|
|
34
|
+
isRunning = false;
|
|
35
|
+
});
|
|
36
|
+
}, 100);
|
|
37
|
+
});
|
|
@@ -5,20 +5,20 @@ export function getLastWord(input) {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export function capitalized(input) {
|
|
8
|
-
|
|
8
|
+
const formatted = input.replace(
|
|
9
9
|
/\w*/g,
|
|
10
10
|
function(txt) {
|
|
11
11
|
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
|
12
12
|
})
|
|
13
|
-
|
|
13
|
+
return formatted.replace(/,/g, '');
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function kebabCased(input) {
|
|
17
|
-
|
|
17
|
+
return input.replace(/[A-Z][a-z]*/g, str => str.toLowerCase() + ' ').trim().replace(/ /g, '-').replace(/,/g, '');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function camelCased(input) {
|
|
21
|
-
|
|
21
|
+
return input
|
|
22
22
|
.split('-')
|
|
23
23
|
.map((word, index) =>
|
|
24
24
|
index === 0
|
|
@@ -36,10 +36,10 @@ export function pascalCased(input) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function allFormats(input) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
return {
|
|
40
|
+
kebab: kebabCased(input),
|
|
41
|
+
pascal: pascalCased(input),
|
|
42
|
+
camel: camelCased(input),
|
|
43
|
+
capitalized: capitalized(input)
|
|
44
|
+
}
|
|
45
45
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getLastWord, capitalized, kebabCased, camelCased, pascalCased, allFormats } from './format.js';
|
|
2
|
+
|
|
3
|
+
describe('format', () => {
|
|
4
|
+
it('getLastWord returns the last non-empty path segment', () => {
|
|
5
|
+
expect(getLastWord('a/b/c')).toBe('c');
|
|
6
|
+
expect(getLastWord('a/b/c/')).toBe('c');
|
|
7
|
+
expect(getLastWord('single')).toBe('single');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('getLastWord returns an empty string for non-string input', () => {
|
|
11
|
+
expect(getLastWord(123)).toBe('');
|
|
12
|
+
expect(getLastWord(undefined)).toBe('');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('capitalized title-cases each word and strips commas', () => {
|
|
16
|
+
expect(capitalized('hello world')).toBe('Hello World');
|
|
17
|
+
expect(capitalized('HELLO, WORLd')).toBe('Hello World');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('kebabCased splits on capital letters', () => {
|
|
21
|
+
expect(kebabCased('HelloWorld')).toBe('hello-world');
|
|
22
|
+
// leading lowercase run isn't matched by the [A-Z][a-z]* regex, so it
|
|
23
|
+
// sticks to the next captured word instead of being split off on its own
|
|
24
|
+
expect(kebabCased('myComponentName')).toBe('mycomponent-name');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('camelCased joins kebab-case segments with an initial lowercase', () => {
|
|
28
|
+
expect(camelCased('my-component-name')).toBe('myComponentName');
|
|
29
|
+
expect(camelCased('single')).toBe('single');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('pascalCased joins kebab-case segments with every segment capitalized', () => {
|
|
33
|
+
expect(pascalCased('my-component-name')).toBe('MyComponentName');
|
|
34
|
+
expect(pascalCased('single')).toBe('Single');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('allFormats returns all four casings for the same input', () => {
|
|
38
|
+
expect(allFormats('my-component')).toEqual({
|
|
39
|
+
kebab: 'my-component',
|
|
40
|
+
pascal: 'MyComponent',
|
|
41
|
+
camel: 'myComponent',
|
|
42
|
+
capitalized: 'My-Component',
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -16,7 +16,12 @@ export function getFilesRecursive(dir, extension) {
|
|
|
16
16
|
|
|
17
17
|
function findFilesRecursively(currentDir) {
|
|
18
18
|
const files = [];
|
|
19
|
-
|
|
19
|
+
let items;
|
|
20
|
+
try {
|
|
21
|
+
items = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
22
|
+
} catch (err) {
|
|
23
|
+
throw new Error(`Failed to read directory "${currentDir}": ${err.message}`, { cause: err });
|
|
24
|
+
}
|
|
20
25
|
|
|
21
26
|
for (const item of items) {
|
|
22
27
|
const fullPath = path.join(currentDir, item.name);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { getFilesRecursive } from './get-files-recursive.js';
|
|
5
|
+
|
|
6
|
+
describe('getFilesRecursive', () => {
|
|
7
|
+
let tmpDir;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nutin-get-files-recursive-'));
|
|
11
|
+
fs.mkdirSync(path.join(tmpDir, 'nested'), { recursive: true });
|
|
12
|
+
fs.writeFileSync(path.join(tmpDir, 'a.js'), '');
|
|
13
|
+
fs.writeFileSync(path.join(tmpDir, 'b.css'), '');
|
|
14
|
+
fs.writeFileSync(path.join(tmpDir, 'nested', 'c.js'), '');
|
|
15
|
+
fs.writeFileSync(path.join(tmpDir, 'nested', 'd.txt'), '');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('recursively collects files matching a single extension', () => {
|
|
23
|
+
const files = getFilesRecursive(tmpDir, '.js').sort();
|
|
24
|
+
expect(files).toEqual([
|
|
25
|
+
path.join(tmpDir, 'a.js'),
|
|
26
|
+
path.join(tmpDir, 'nested', 'c.js'),
|
|
27
|
+
].sort());
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('accepts an extension without a leading dot', () => {
|
|
31
|
+
const files = getFilesRecursive(tmpDir, 'js').sort();
|
|
32
|
+
expect(files).toEqual([
|
|
33
|
+
path.join(tmpDir, 'a.js'),
|
|
34
|
+
path.join(tmpDir, 'nested', 'c.js'),
|
|
35
|
+
].sort());
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('accepts an array of extensions', () => {
|
|
39
|
+
const files = getFilesRecursive(tmpDir, ['.css', '.txt']).sort();
|
|
40
|
+
expect(files).toEqual([
|
|
41
|
+
path.join(tmpDir, 'b.css'),
|
|
42
|
+
path.join(tmpDir, 'nested', 'd.txt'),
|
|
43
|
+
].sort());
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('returns an empty array when the directory does not exist', () => {
|
|
47
|
+
expect(getFilesRecursive(path.join(tmpDir, 'does-not-exist'), '.js')).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { getRelToCore } from './get-rel-to-core.js';
|
|
3
|
+
|
|
4
|
+
describe('getRelToCore', () => {
|
|
5
|
+
it('adds one "../" per path segment before core/index.js', () => {
|
|
6
|
+
const targetPath = ['src', 'app', 'components', 'widget'].join(path.sep);
|
|
7
|
+
expect(getRelToCore(targetPath)).toBe('../../../core/index.js');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('adds no "../" for a single-segment path', () => {
|
|
11
|
+
expect(getRelToCore('widget')).toBe('core/index.js');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('scales with the number of segments', () => {
|
|
15
|
+
const targetPath = ['a', 'b'].join(path.sep);
|
|
16
|
+
expect(getRelToCore(targetPath)).toBe('../core/index.js');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -3,8 +3,6 @@ export * from './format.js';
|
|
|
3
3
|
export * from './get-files-recursive.js';
|
|
4
4
|
export * from './run-command.js';
|
|
5
5
|
export * from './run-script.js';
|
|
6
|
-
export * from './export-flags.js';
|
|
7
6
|
export * from './prompt-boolean.js';
|
|
8
|
-
{{#if i18n}}
|
|
9
7
|
export * from './languages.js';
|
|
10
|
-
|
|
8
|
+
export * from './error-exit.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { errorExit } from './error-exit.js';
|
|
4
|
+
|
|
5
|
+
const langsPath = path.join(process.cwd(), 'config', 'languages.json');
|
|
6
|
+
|
|
7
|
+
let langs;
|
|
8
|
+
try {
|
|
9
|
+
langs = JSON.parse(fs.readFileSync(langsPath, 'utf-8'));
|
|
10
|
+
} catch (err) {
|
|
11
|
+
errorExit(new Error(`config/languages.json is missing or malformed: ${err.message}`), 'languages');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const LANGUAGES = langs.languages;
|
|
15
|
+
export const DEFAULT_LANGUAGE = langs.defaultLanguage;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { LANGUAGES, DEFAULT_LANGUAGE } from './languages.js';
|
|
2
|
+
|
|
3
|
+
describe('languages', () => {
|
|
4
|
+
it('reads LANGUAGES from config/languages.json', () => {
|
|
5
|
+
expect(Array.isArray(LANGUAGES)).toBeTruthy();
|
|
6
|
+
expect(LANGUAGES.length).toBeGreaterThan(0);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('reads DEFAULT_LANGUAGE from config/languages.json', () => {
|
|
10
|
+
expect(typeof DEFAULT_LANGUAGE).toBe('string');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('includes the default language among the supported languages', () => {
|
|
14
|
+
expect(LANGUAGES).toContain(DEFAULT_LANGUAGE);
|
|
15
|
+
});
|
|
16
|
+
});
|