@zeix/le-truc 0.15.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/.ai-context.md +234 -0
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
- package/.editorconfig +12 -0
- package/.github/copilot-instructions.md +62 -0
- package/.github/workflows/codeql.yml +108 -0
- package/.github/workflows/static.yml +43 -0
- package/.prettierrc +17 -0
- package/CLAUDE.md +215 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +160 -0
- package/LICENSE +21 -0
- package/README.md +474 -0
- package/biome.json +295 -0
- package/bun.lock +239 -0
- package/docs/about.html +105 -0
- package/docs/assets/main.css +1 -0
- package/docs/assets/main.js +10 -0
- package/docs/assets/main.js.map +66 -0
- package/docs/components.html +293 -0
- package/docs/data-flow.html +308 -0
- package/docs/examples/basic-button.html +367 -0
- package/docs/examples/basic-counter.html +188 -0
- package/docs/examples/basic-hello.html +138 -0
- package/docs/examples/basic-number.html +271 -0
- package/docs/examples/basic-pluralize.html +214 -0
- package/docs/examples/card-callout.html +152 -0
- package/docs/examples/card-mediaqueries.html +138 -0
- package/docs/examples/context-media.html +198 -0
- package/docs/examples/empty.html +37 -0
- package/docs/examples/form-checkbox.html +233 -0
- package/docs/examples/form-combobox.html +420 -0
- package/docs/examples/form-listbox.html +434 -0
- package/docs/examples/form-radiogroup.html +296 -0
- package/docs/examples/form-spinbutton.html +402 -0
- package/docs/examples/form-textbox.html +361 -0
- package/docs/examples/layout.html +67 -0
- package/docs/examples/module-carousel.html +552 -0
- package/docs/examples/module-catalog.html +241 -0
- package/docs/examples/module-codeblock.html +270 -0
- package/docs/examples/module-dialog.html +343 -0
- package/docs/examples/module-lazyload.html +289 -0
- package/docs/examples/module-list.html +197 -0
- package/docs/examples/module-pagination.html +283 -0
- package/docs/examples/module-scrollarea.html +447 -0
- package/docs/examples/module-tabgroup.html +526 -0
- package/docs/examples/module-todo.html +367 -0
- package/docs/examples/module-with-type.html +63 -0
- package/docs/examples/nested-components.html +88 -0
- package/docs/examples/recursive.html +56 -0
- package/docs/examples/simple-text.html +39 -0
- package/docs/examples/snippet.html +93 -0
- package/docs/examples/with-styles.html +75 -0
- package/docs/getting-started.html +143 -0
- package/docs/index.html +112 -0
- package/docs/sitemap.xml +28 -0
- package/docs/styling.html +160 -0
- package/docs/sw.js +112 -0
- package/docs-src/api/README.md +478 -0
- package/docs-src/api/_media/LICENSE +21 -0
- package/docs-src/api/classes/CircularDependencyError.md +299 -0
- package/docs-src/api/classes/CircularMutationError.md +301 -0
- package/docs-src/api/classes/ContextRequestEvent.md +590 -0
- package/docs-src/api/classes/DependencyTimeoutError.md +301 -0
- package/docs-src/api/classes/InvalidCallbackError.md +303 -0
- package/docs-src/api/classes/InvalidComponentNameError.md +295 -0
- package/docs-src/api/classes/InvalidCustomElementError.md +301 -0
- package/docs-src/api/classes/InvalidEffectsError.md +301 -0
- package/docs-src/api/classes/InvalidPropertyNameError.md +307 -0
- package/docs-src/api/classes/InvalidReactivesError.md +307 -0
- package/docs-src/api/classes/InvalidSignalValueError.md +303 -0
- package/docs-src/api/classes/MissingElementError.md +307 -0
- package/docs-src/api/classes/NullishSignalValueError.md +299 -0
- package/docs-src/api/classes/StoreKeyExistsError.md +303 -0
- package/docs-src/api/classes/StoreKeyRangeError.md +299 -0
- package/docs-src/api/classes/StoreKeyReadonlyError.md +303 -0
- package/docs-src/api/functions/asBoolean.md +21 -0
- package/docs-src/api/functions/asEnum.md +31 -0
- package/docs-src/api/functions/asInteger.md +39 -0
- package/docs-src/api/functions/asJSON.md +49 -0
- package/docs-src/api/functions/asNumber.md +37 -0
- package/docs-src/api/functions/asString.md +37 -0
- package/docs-src/api/functions/createCollection.md +83 -0
- package/docs-src/api/functions/createSensor.md +71 -0
- package/docs-src/api/functions/dangerouslySetInnerHTML.md +48 -0
- package/docs-src/api/functions/defineComponent.md +65 -0
- package/docs-src/api/functions/isCollection.md +37 -0
- package/docs-src/api/functions/isParser.md +41 -0
- package/docs-src/api/functions/match.md +47 -0
- package/docs-src/api/functions/on.md +58 -0
- package/docs-src/api/functions/pass.md +53 -0
- package/docs-src/api/functions/provideContexts.md +47 -0
- package/docs-src/api/functions/read.md +47 -0
- package/docs-src/api/functions/requestContext.md +51 -0
- package/docs-src/api/functions/resolve.md +40 -0
- package/docs-src/api/functions/runEffects.md +51 -0
- package/docs-src/api/functions/runElementEffects.md +57 -0
- package/docs-src/api/functions/schedule.md +33 -0
- package/docs-src/api/functions/setAttribute.md +48 -0
- package/docs-src/api/functions/setProperty.md +52 -0
- package/docs-src/api/functions/setStyle.md +48 -0
- package/docs-src/api/functions/setText.md +42 -0
- package/docs-src/api/functions/show.md +42 -0
- package/docs-src/api/functions/toSignal.md +37 -0
- package/docs-src/api/functions/toggleAttribute.md +48 -0
- package/docs-src/api/functions/toggleClass.md +48 -0
- package/docs-src/api/functions/updateElement.md +53 -0
- package/docs-src/api/globals.md +131 -0
- package/docs-src/api/type-aliases/Cleanup.md +27 -0
- package/docs-src/api/type-aliases/Collection.md +91 -0
- package/docs-src/api/type-aliases/CollectionListener.md +27 -0
- package/docs-src/api/type-aliases/Component.md +17 -0
- package/docs-src/api/type-aliases/ComponentProp.md +11 -0
- package/docs-src/api/type-aliases/ComponentProps.md +11 -0
- package/docs-src/api/type-aliases/ComponentSetup.md +31 -0
- package/docs-src/api/type-aliases/ComponentUI.md +27 -0
- package/docs-src/api/type-aliases/Computed.md +49 -0
- package/docs-src/api/type-aliases/ComputedCallback.md +29 -0
- package/docs-src/api/type-aliases/Context.md +33 -0
- package/docs-src/api/type-aliases/ContextType.md +19 -0
- package/docs-src/api/type-aliases/DangerouslySetInnerHTMLOptions.md +27 -0
- package/docs-src/api/type-aliases/DiffResult.md +61 -0
- package/docs-src/api/type-aliases/Effect.md +35 -0
- package/docs-src/api/type-aliases/EffectCallback.md +23 -0
- package/docs-src/api/type-aliases/Effects.md +21 -0
- package/docs-src/api/type-aliases/ElementEffects.md +21 -0
- package/docs-src/api/type-aliases/ElementFromKey.md +21 -0
- package/docs-src/api/type-aliases/ElementQueries.md +27 -0
- package/docs-src/api/type-aliases/ElementUpdater.md +131 -0
- package/docs-src/api/type-aliases/EventHandler.md +31 -0
- package/docs-src/api/type-aliases/EventType.md +17 -0
- package/docs-src/api/type-aliases/Fallback.md +21 -0
- package/docs-src/api/type-aliases/Initializers.md +21 -0
- package/docs-src/api/type-aliases/LooseReader.md +31 -0
- package/docs-src/api/type-aliases/MatchHandlers.md +77 -0
- package/docs-src/api/type-aliases/MaybeCleanup.md +23 -0
- package/docs-src/api/type-aliases/MaybeSignal.md +17 -0
- package/docs-src/api/type-aliases/Parser.md +39 -0
- package/docs-src/api/type-aliases/ParserOrFallback.md +21 -0
- package/docs-src/api/type-aliases/PassedProp.md +25 -0
- package/docs-src/api/type-aliases/PassedProps.md +21 -0
- package/docs-src/api/type-aliases/Reactive.md +25 -0
- package/docs-src/api/type-aliases/Reader.md +31 -0
- package/docs-src/api/type-aliases/ReservedWords.md +11 -0
- package/docs-src/api/type-aliases/ResolveResult.md +29 -0
- package/docs-src/api/type-aliases/SensorEvents.md +25 -0
- package/docs-src/api/type-aliases/Signal.md +41 -0
- package/docs-src/api/type-aliases/State.md +85 -0
- package/docs-src/api/type-aliases/Store.md +29 -0
- package/docs-src/api/type-aliases/UI.md +11 -0
- package/docs-src/api/type-aliases/UnknownContext.md +13 -0
- package/docs-src/api/variables/CONTEXT_REQUEST.md +11 -0
- package/docs-src/api/variables/UNSET.md +23 -0
- package/docs-src/api/variables/batch.md +25 -0
- package/docs-src/api/variables/createComputed.md +41 -0
- package/docs-src/api/variables/createEffect.md +35 -0
- package/docs-src/api/variables/createState.md +37 -0
- package/docs-src/api/variables/createStore.md +42 -0
- package/docs-src/api/variables/diff.md +43 -0
- package/docs-src/api/variables/isAbortError.md +33 -0
- package/docs-src/api/variables/isAsyncFunction.md +39 -0
- package/docs-src/api/variables/isComputed.md +37 -0
- package/docs-src/api/variables/isEqual.md +49 -0
- package/docs-src/api/variables/isFunction.md +39 -0
- package/docs-src/api/variables/isMutableSignal.md +37 -0
- package/docs-src/api/variables/isNumber.md +33 -0
- package/docs-src/api/variables/isRecord.md +39 -0
- package/docs-src/api/variables/isRecordOrArray.md +39 -0
- package/docs-src/api/variables/isSignal.md +37 -0
- package/docs-src/api/variables/isState.md +37 -0
- package/docs-src/api/variables/isStore.md +37 -0
- package/docs-src/api/variables/isString.md +33 -0
- package/docs-src/api/variables/isSymbol.md +33 -0
- package/docs-src/api/variables/toError.md +33 -0
- package/docs-src/api/variables/valueString.md +33 -0
- package/docs-src/includes/menu.html +44 -0
- package/docs-src/pages/about.md +89 -0
- package/docs-src/pages/components.md +437 -0
- package/docs-src/pages/data-flow.md +449 -0
- package/docs-src/pages/getting-started.md +170 -0
- package/docs-src/pages/index.md +98 -0
- package/docs-src/pages/styling.md +165 -0
- package/eslint.config.js +64 -0
- package/examples/_common/clear.ts +49 -0
- package/examples/_common/fetch.ts +160 -0
- package/examples/_common/focus.ts +45 -0
- package/examples/_common/highlight.ts +5 -0
- package/examples/_global.css +463 -0
- package/examples/basic-button/basic-button.css +176 -0
- package/examples/basic-button/basic-button.html +46 -0
- package/examples/basic-button/basic-button.spec.ts +160 -0
- package/examples/basic-button/basic-button.ts +45 -0
- package/examples/basic-button/copyToClipboard.ts +37 -0
- package/examples/basic-counter/basic-counter.css +21 -0
- package/examples/basic-counter/basic-counter.html +24 -0
- package/examples/basic-counter/basic-counter.spec.ts +85 -0
- package/examples/basic-counter/basic-counter.ts +43 -0
- package/examples/basic-hello/basic-hello.html +34 -0
- package/examples/basic-hello/basic-hello.spec.ts +110 -0
- package/examples/basic-hello/basic-hello.ts +36 -0
- package/examples/basic-number/basic-number.html +79 -0
- package/examples/basic-number/basic-number.spec.ts +175 -0
- package/examples/basic-number/basic-number.ts +124 -0
- package/examples/basic-pluralize/basic-pluralize.html +64 -0
- package/examples/basic-pluralize/basic-pluralize.spec.ts +258 -0
- package/examples/basic-pluralize/basic-pluralize.ts +82 -0
- package/examples/card-callout/card-callout.css +79 -0
- package/examples/card-callout/card-callout.html +5 -0
- package/examples/card-mediaqueries/card-mediaqueries.html +29 -0
- package/examples/card-mediaqueries/card-mediaqueries.spec.ts +300 -0
- package/examples/card-mediaqueries/card-mediaqueries.ts +41 -0
- package/examples/context-media/context-media.html +3 -0
- package/examples/context-media/context-media.ts +127 -0
- package/examples/form-checkbox/form-checkbox.css +70 -0
- package/examples/form-checkbox/form-checkbox.html +13 -0
- package/examples/form-checkbox/form-checkbox.spec.ts +357 -0
- package/examples/form-checkbox/form-checkbox.ts +50 -0
- package/examples/form-checkbox/vanilla-checkbox.ts +101 -0
- package/examples/form-combobox/form-combobox.css +118 -0
- package/examples/form-combobox/form-combobox.html +74 -0
- package/examples/form-combobox/form-combobox.spec.ts +977 -0
- package/examples/form-combobox/form-combobox.ts +128 -0
- package/examples/form-listbox/form-listbox.css +71 -0
- package/examples/form-listbox/form-listbox.html +67 -0
- package/examples/form-listbox/form-listbox.spec.ts +1050 -0
- package/examples/form-listbox/form-listbox.ts +196 -0
- package/examples/form-listbox/mocks/timezones.json +495 -0
- package/examples/form-radiogroup/form-radiogroup.css +87 -0
- package/examples/form-radiogroup/form-radiogroup.html +51 -0
- package/examples/form-radiogroup/form-radiogroup.spec.ts +515 -0
- package/examples/form-radiogroup/form-radiogroup.ts +58 -0
- package/examples/form-spinbutton/form-spinbutton.css +95 -0
- package/examples/form-spinbutton/form-spinbutton.html +96 -0
- package/examples/form-spinbutton/form-spinbutton.spec.ts +688 -0
- package/examples/form-spinbutton/form-spinbutton.ts +111 -0
- package/examples/form-textbox/form-textbox.css +104 -0
- package/examples/form-textbox/form-textbox.html +53 -0
- package/examples/form-textbox/form-textbox.spec.ts +542 -0
- package/examples/form-textbox/form-textbox.ts +104 -0
- package/examples/main.css +22 -0
- package/examples/main.ts +23 -0
- package/examples/module-carousel/module-carousel.css +113 -0
- package/examples/module-carousel/module-carousel.html +208 -0
- package/examples/module-carousel/module-carousel.spec.ts +523 -0
- package/examples/module-carousel/module-carousel.ts +131 -0
- package/examples/module-catalog/module-catalog.css +22 -0
- package/examples/module-catalog/module-catalog.html +82 -0
- package/examples/module-catalog/module-catalog.spec.ts +396 -0
- package/examples/module-catalog/module-catalog.ts +37 -0
- package/examples/module-codeblock/module-codeblock.css +95 -0
- package/examples/module-codeblock/module-codeblock.html +28 -0
- package/examples/module-codeblock/module-codeblock.ts +47 -0
- package/examples/module-demo/module-demo.css +13 -0
- package/examples/module-dialog/module-dialog.css +96 -0
- package/examples/module-dialog/module-dialog.html +66 -0
- package/examples/module-dialog/module-dialog.spec.ts +557 -0
- package/examples/module-dialog/module-dialog.ts +81 -0
- package/examples/module-lazyload/mocks/empty.html +1 -0
- package/examples/module-lazyload/mocks/module-with-type.html +27 -0
- package/examples/module-lazyload/mocks/nested-components.html +52 -0
- package/examples/module-lazyload/mocks/recursive.html +20 -0
- package/examples/module-lazyload/mocks/simple-text.html +3 -0
- package/examples/module-lazyload/mocks/snippet.html +57 -0
- package/examples/module-lazyload/mocks/with-styles.html +39 -0
- package/examples/module-lazyload/module-lazyload.html +132 -0
- package/examples/module-lazyload/module-lazyload.spec.ts +734 -0
- package/examples/module-lazyload/module-lazyload.ts +89 -0
- package/examples/module-list/module-list.html +30 -0
- package/examples/module-list/module-list.spec.ts +592 -0
- package/examples/module-list/module-list.ts +99 -0
- package/examples/module-pagination/module-pagination.css +79 -0
- package/examples/module-pagination/module-pagination.html +16 -0
- package/examples/module-pagination/module-pagination.spec.ts +701 -0
- package/examples/module-pagination/module-pagination.ts +88 -0
- package/examples/module-scrollarea/module-scrollarea.css +77 -0
- package/examples/module-scrollarea/module-scrollarea.html +189 -0
- package/examples/module-scrollarea/module-scrollarea.spec.ts +445 -0
- package/examples/module-scrollarea/module-scrollarea.ts +81 -0
- package/examples/module-tabgroup/module-tabgroup.css +55 -0
- package/examples/module-tabgroup/module-tabgroup.html +269 -0
- package/examples/module-tabgroup/module-tabgroup.spec.ts +631 -0
- package/examples/module-tabgroup/module-tabgroup.ts +102 -0
- package/examples/module-toc/module-toc.css +34 -0
- package/examples/module-todo/module-todo.css +84 -0
- package/examples/module-todo/module-todo.html +92 -0
- package/examples/module-todo/module-todo.spec.ts +528 -0
- package/examples/module-todo/module-todo.ts +91 -0
- package/examples/section-hero/section-hero.css +37 -0
- package/examples/section-menu/section-menu.css +81 -0
- package/examples/server.ts +95 -0
- package/examples/test-setup.md +314 -0
- package/index.dev.js +1688 -0
- package/index.dev.ts +127 -0
- package/index.js +3 -0
- package/index.js.map +42 -0
- package/index.ts +127 -0
- package/package.json +64 -0
- package/playwright.config.ts +31 -0
- package/server/BUILD_SYSTEM.md +428 -0
- package/server/SERVER.md +286 -0
- package/server/build.ts +91 -0
- package/server/config.ts +130 -0
- package/server/effects/api.ts +28 -0
- package/server/effects/css.ts +31 -0
- package/server/effects/examples.ts +109 -0
- package/server/effects/js.ts +32 -0
- package/server/effects/menu.ts +34 -0
- package/server/effects/pages.ts +178 -0
- package/server/effects/service-worker.ts +57 -0
- package/server/effects/sitemap.ts +27 -0
- package/server/file-signals.ts +361 -0
- package/server/file-watcher.ts +77 -0
- package/server/io.ts +174 -0
- package/server/layout-engine.ts +470 -0
- package/server/layout-utils.ts +615 -0
- package/server/layouts/api.html +76 -0
- package/server/layouts/base.html +37 -0
- package/server/layouts/blog.html +115 -0
- package/server/layouts/example.html +104 -0
- package/server/layouts/overview.html +165 -0
- package/server/layouts/page.html +36 -0
- package/server/layouts/test.html +24 -0
- package/server/markdoc-helpers.ts +217 -0
- package/server/markdoc.config.ts +29 -0
- package/server/schema/callout.markdoc.ts +17 -0
- package/server/schema/carousel.markdoc.ts +118 -0
- package/server/schema/demo.markdoc.ts +74 -0
- package/server/schema/fence.markdoc.ts +84 -0
- package/server/schema/heading.markdoc.ts +23 -0
- package/server/schema/hero.markdoc.ts +59 -0
- package/server/schema/section.markdoc.ts +10 -0
- package/server/schema/slide.markdoc.ts +17 -0
- package/server/schema/source.markdoc.ts +53 -0
- package/server/schema/tabgroup.markdoc.ts +102 -0
- package/server/serve.ts +635 -0
- package/server/templates/README.md +352 -0
- package/server/templates/constants.ts +236 -0
- package/server/templates/fragments.ts +159 -0
- package/server/templates/hmr.ts +269 -0
- package/server/templates/menu.ts +33 -0
- package/server/templates/performance-hints.ts +94 -0
- package/server/templates/service-worker.ts +403 -0
- package/server/templates/sitemap.ts +57 -0
- package/server/templates/toc.ts +41 -0
- package/server/templates/utils.ts +378 -0
- package/src/component.ts +215 -0
- package/src/context.ts +156 -0
- package/src/effects/attribute.ts +82 -0
- package/src/effects/class.ts +28 -0
- package/src/effects/event.ts +67 -0
- package/src/effects/html.ts +60 -0
- package/src/effects/method.ts +57 -0
- package/src/effects/pass.ts +103 -0
- package/src/effects/property.ts +57 -0
- package/src/effects/style.ts +34 -0
- package/src/effects/text.ts +28 -0
- package/src/effects.ts +412 -0
- package/src/errors.ts +160 -0
- package/src/parsers/boolean.ts +14 -0
- package/src/parsers/json.ts +33 -0
- package/src/parsers/number.ts +55 -0
- package/src/parsers/string.ts +32 -0
- package/src/parsers.ts +90 -0
- package/src/scheduler.ts +47 -0
- package/src/signals/collection.ts +253 -0
- package/src/signals/sensor.ts +131 -0
- package/src/ui.ts +236 -0
- package/src/util.ts +187 -0
- package/tsconfig.json +34 -0
- package/types/examples/basic-button/basic-button.d.ts +16 -0
- package/types/examples/basic-hello/basic-hello.d.ts +18 -0
- package/types/index.d.ts +27 -0
- package/types/index.dev.d.ts +27 -0
- package/types/src/collection.d.ts +27 -0
- package/types/src/component.d.ts +32 -0
- package/types/src/context.d.ts +85 -0
- package/types/src/effects/attribute.d.ts +23 -0
- package/types/src/effects/callMethod.d.ts +23 -0
- package/types/src/effects/class.d.ts +13 -0
- package/types/src/effects/dangerouslySetInnerHTML.d.ts +18 -0
- package/types/src/effects/event.d.ts +18 -0
- package/types/src/effects/html.d.ts +17 -0
- package/types/src/effects/method.d.ts +22 -0
- package/types/src/effects/pass.d.ts +18 -0
- package/types/src/effects/property.d.ts +22 -0
- package/types/src/effects/setAttribute.d.ts +24 -0
- package/types/src/effects/setProperty.d.ts +23 -0
- package/types/src/effects/setStyle.d.ts +14 -0
- package/types/src/effects/setText.d.ts +13 -0
- package/types/src/effects/style.d.ts +13 -0
- package/types/src/effects/text.d.ts +12 -0
- package/types/src/effects/toggleClass.d.ts +14 -0
- package/types/src/effects.d.ts +153 -0
- package/types/src/errors.d.ts +99 -0
- package/types/src/events.d.ts +27 -0
- package/types/src/extractors.d.ts +23 -0
- package/types/src/parsers/boolean.d.ts +10 -0
- package/types/src/parsers/json.d.ts +13 -0
- package/types/src/parsers/number.d.ts +21 -0
- package/types/src/parsers/string.d.ts +19 -0
- package/types/src/parsers.d.ts +41 -0
- package/types/src/scheduler.d.ts +11 -0
- package/types/src/sensor.d.ts +27 -0
- package/types/src/signals/collection.d.ts +32 -0
- package/types/src/signals/sensor.d.ts +27 -0
- package/types/src/ui.d.ts +37 -0
- package/types/src/util.d.ts +65 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
test.describe('basic-number component', () => {
|
|
4
|
+
test.beforeEach(async ({ page }) => {
|
|
5
|
+
page.on('console', msg => {
|
|
6
|
+
console.log(`[browser] ${msg.type()}: ${msg.text()}`)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
await page.goto('http://localhost:3000/test/basic-number.html')
|
|
10
|
+
await page.waitForSelector('basic-number')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('renders default number formatting', async ({ page }) => {
|
|
14
|
+
// Test the first example: basic unit formatting
|
|
15
|
+
const firstNumber = page.locator('basic-number').first()
|
|
16
|
+
await expect(firstNumber).toHaveText('25,678.9 liters')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('formats currency with locale-specific formatting', async ({ page }) => {
|
|
20
|
+
// Test German-Swiss currency formatting
|
|
21
|
+
const germanNumber = page.locator('#german-swiss')
|
|
22
|
+
await expect(germanNumber).toContainText('CHF') // Should contain Swiss Franc
|
|
23
|
+
await expect(germanNumber).toContainText('25') // Should contain the base number
|
|
24
|
+
|
|
25
|
+
// Test French-Swiss currency formatting
|
|
26
|
+
const frenchNumber = page.locator('#french-swiss')
|
|
27
|
+
await expect(frenchNumber).toContainText('CHF') // Should contain Swiss Franc
|
|
28
|
+
await expect(frenchNumber).toContainText('25') // Should contain the base number
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('handles different unit types and locales', async ({ page }) => {
|
|
32
|
+
// Test Arabic locale with speed unit - expect Arabic-Indic numerals
|
|
33
|
+
const arabicNumber = page.locator('#arabic-speed')
|
|
34
|
+
await expect(arabicNumber).toContainText('٢٥') // Should contain Arabic-Indic numerals for 25
|
|
35
|
+
|
|
36
|
+
// Test Chinese locale with time unit - should contain time unit and some form of the number
|
|
37
|
+
const chineseNumber = page.locator('#chinese-time')
|
|
38
|
+
await expect(chineseNumber).toContainText('秒') // Should contain Chinese word for "second"
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('updates when value property changes', async ({ page }) => {
|
|
42
|
+
const numberElement = page.locator('basic-number').first()
|
|
43
|
+
|
|
44
|
+
// Get initial text
|
|
45
|
+
const initialText = await numberElement.textContent()
|
|
46
|
+
expect(initialText).toContain('25,678.9')
|
|
47
|
+
|
|
48
|
+
// Change the value property
|
|
49
|
+
await numberElement.evaluate(node => {
|
|
50
|
+
;(node as any).value = 12345.6
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Should update to show new formatted number
|
|
54
|
+
await expect(numberElement).toHaveText('12,345.6 liters')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('handles invalid JSON options gracefully', async ({ page }) => {
|
|
58
|
+
// Test HTML markup error logging - should log error on page load
|
|
59
|
+
const htmlElement = page.locator('#invalid-json-html')
|
|
60
|
+
await expect(htmlElement).toHaveText('1,234.5') // Should fall back to default
|
|
61
|
+
|
|
62
|
+
// Test dynamic creation also logs errors
|
|
63
|
+
const consoleMessages: string[] = []
|
|
64
|
+
page.on('console', msg => {
|
|
65
|
+
if (msg.type() === 'error') {
|
|
66
|
+
consoleMessages.push(msg.text())
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
await page.evaluate(() => {
|
|
71
|
+
const element = document.createElement('basic-number') as any
|
|
72
|
+
element.value = 9999
|
|
73
|
+
element.setAttribute('options', '{ invalid json }')
|
|
74
|
+
element.setAttribute('id', 'invalid-json-dynamic')
|
|
75
|
+
document.body.appendChild(element)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const dynamicElement = page.locator('#invalid-json-dynamic')
|
|
79
|
+
await expect(dynamicElement).toHaveText('9,999') // Should fall back to default
|
|
80
|
+
|
|
81
|
+
// Verify console error was logged for dynamic creation
|
|
82
|
+
const hasJsonError = consoleMessages.some(
|
|
83
|
+
msg => msg.includes('Invalid JSON') || msg.includes('JSON'),
|
|
84
|
+
)
|
|
85
|
+
expect(hasJsonError).toBe(true)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('handles missing required currency gracefully', async ({ page }) => {
|
|
89
|
+
// Test HTML markup error logging - should log error on page load
|
|
90
|
+
const htmlElement = page.locator('#missing-currency-html')
|
|
91
|
+
await expect(htmlElement).toHaveText('1,000') // Should fall back to default
|
|
92
|
+
|
|
93
|
+
// Test dynamic creation also logs errors
|
|
94
|
+
const consoleMessages: string[] = []
|
|
95
|
+
page.on('console', msg => {
|
|
96
|
+
if (msg.type() === 'error') {
|
|
97
|
+
consoleMessages.push(msg.text())
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
await page.evaluate(() => {
|
|
102
|
+
const element = document.createElement('basic-number') as any
|
|
103
|
+
element.value = 9999
|
|
104
|
+
element.setAttribute('options', '{"style":"currency"}')
|
|
105
|
+
element.setAttribute('id', 'missing-currency-dynamic')
|
|
106
|
+
document.body.appendChild(element)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const dynamicElement = page.locator('#missing-currency-dynamic')
|
|
110
|
+
await expect(dynamicElement).toHaveText('9,999') // Should fall back to default
|
|
111
|
+
|
|
112
|
+
// Verify console error was logged for dynamic creation
|
|
113
|
+
const hasCurrencyError = consoleMessages.some(
|
|
114
|
+
msg => msg.includes('currency') && msg.includes('CHF'),
|
|
115
|
+
)
|
|
116
|
+
expect(hasCurrencyError).toBe(true)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('handles missing required unit gracefully', async ({ page }) => {
|
|
120
|
+
// Test HTML markup error logging - should log error on page load
|
|
121
|
+
const htmlElement = page.locator('#missing-unit-html')
|
|
122
|
+
await expect(htmlElement).toHaveText('500') // Should fall back to default
|
|
123
|
+
|
|
124
|
+
// Test dynamic creation also logs errors
|
|
125
|
+
const consoleMessages: string[] = []
|
|
126
|
+
page.on('console', msg => {
|
|
127
|
+
if (msg.type() === 'error') {
|
|
128
|
+
consoleMessages.push(msg.text())
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
await page.evaluate(() => {
|
|
133
|
+
const element = document.createElement('basic-number') as any
|
|
134
|
+
element.value = 9999
|
|
135
|
+
element.setAttribute('options', '{"style":"unit"}')
|
|
136
|
+
element.setAttribute('id', 'missing-unit-dynamic')
|
|
137
|
+
document.body.appendChild(element)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const dynamicElement = page.locator('#missing-unit-dynamic')
|
|
141
|
+
await expect(dynamicElement).toHaveText('9,999') // Should fall back to default
|
|
142
|
+
|
|
143
|
+
// Verify console error was logged for dynamic creation
|
|
144
|
+
const hasUnitError = consoleMessages.some(
|
|
145
|
+
msg =>
|
|
146
|
+
msg.includes('unit')
|
|
147
|
+
&& (msg.includes('liter') || msg.includes('kilometer')),
|
|
148
|
+
)
|
|
149
|
+
expect(hasUnitError).toBe(true)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('works with decimal style formatting', async ({ page }) => {
|
|
153
|
+
// Use the existing decimal test element from HTML
|
|
154
|
+
const decimalElement = page.locator('#decimal-test')
|
|
155
|
+
// Should respect the fraction digit limits
|
|
156
|
+
await expect(decimalElement).toHaveText('1,234.568')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('inherits locale from closest lang attribute', async ({ page }) => {
|
|
160
|
+
// Use the existing inheritance test element from HTML
|
|
161
|
+
const inheritElement = page.locator('#inherit-test')
|
|
162
|
+
// Should use German formatting for currency
|
|
163
|
+
await expect(inheritElement).toContainText('€')
|
|
164
|
+
await expect(inheritElement).toContainText('1') // Should contain the number
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('handles zero and negative values correctly', async ({ page }) => {
|
|
168
|
+
// Use the existing elements from HTML
|
|
169
|
+
const zeroElement = page.locator('#zero-test')
|
|
170
|
+
const negativeElement = page.locator('#negative-test')
|
|
171
|
+
|
|
172
|
+
await expect(zeroElement).toHaveText('0')
|
|
173
|
+
await expect(negativeElement).toHaveText('-1,234.5')
|
|
174
|
+
})
|
|
175
|
+
})
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { asNumber, type Component, defineComponent, setText } from '../..'
|
|
2
|
+
|
|
3
|
+
export type BasicNumberProps = {
|
|
4
|
+
value: number
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
interface HTMLElementTagNameMap {
|
|
9
|
+
'basic-number': Component<BasicNumberProps>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Logger = {
|
|
14
|
+
onWarn: (message: string) => void
|
|
15
|
+
onError: (message: string) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const FALLBACK_LOCALE = 'en'
|
|
19
|
+
|
|
20
|
+
function getNumberFormatter(
|
|
21
|
+
locale: string,
|
|
22
|
+
rawOptions: string | null,
|
|
23
|
+
logger: Logger = {
|
|
24
|
+
onWarn: console.warn,
|
|
25
|
+
onError: console.error,
|
|
26
|
+
},
|
|
27
|
+
) {
|
|
28
|
+
const useFallback = () => new Intl.NumberFormat(locale)
|
|
29
|
+
if (!rawOptions) return useFallback()
|
|
30
|
+
const { onWarn, onError } = logger
|
|
31
|
+
|
|
32
|
+
let o: Intl.NumberFormatOptions = {}
|
|
33
|
+
try {
|
|
34
|
+
o = JSON.parse(rawOptions)
|
|
35
|
+
} catch (error) {
|
|
36
|
+
onError?.(`Invalid JSON: ${error}`)
|
|
37
|
+
return useFallback()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const style = o.style ?? 'decimal'
|
|
41
|
+
|
|
42
|
+
const drops: string[] = []
|
|
43
|
+
if (style === 'currency') {
|
|
44
|
+
if (
|
|
45
|
+
!o.currency ||
|
|
46
|
+
typeof o.currency !== 'string' ||
|
|
47
|
+
o.currency.length !== 3
|
|
48
|
+
) {
|
|
49
|
+
onError?.(
|
|
50
|
+
`style="currency" requires a 3-letter ISO currency (e.g. "CHF").`,
|
|
51
|
+
)
|
|
52
|
+
return useFallback()
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
drops.push('currency', 'currencyDisplay', 'currencySign')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (style === 'unit') {
|
|
59
|
+
if (!o.unit || typeof o.unit !== 'string') {
|
|
60
|
+
onError?.(
|
|
61
|
+
`style="unit" requires a "unit" (e.g. "liter", "kilometer-per-hour").`,
|
|
62
|
+
)
|
|
63
|
+
return useFallback()
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
drops.push('unit', 'unitDisplay')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (o.notation && o.notation !== 'compact') drops.push('compactDisplay')
|
|
70
|
+
|
|
71
|
+
const sanitized: Intl.NumberFormatOptions = {}
|
|
72
|
+
for (const [k, v] of Object.entries(o)) {
|
|
73
|
+
if (!drops.includes(k)) sanitized[k] = v
|
|
74
|
+
else onWarn?.(`Option "${k}" is ignored for style="${style}".`)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { minimumFractionDigits: minFD, maximumFractionDigits: maxFD } =
|
|
78
|
+
sanitized
|
|
79
|
+
if (minFD != null && maxFD != null && minFD > maxFD) {
|
|
80
|
+
onWarn?.(
|
|
81
|
+
`minimumFractionDigits (${minFD}) > maximumFractionDigits (${maxFD}); swapping.`,
|
|
82
|
+
)
|
|
83
|
+
sanitized.minimumFractionDigits = maxFD
|
|
84
|
+
sanitized.maximumFractionDigits = minFD
|
|
85
|
+
}
|
|
86
|
+
const { minimumSignificantDigits: minSD, maximumSignificantDigits: maxSD } =
|
|
87
|
+
sanitized
|
|
88
|
+
if (minSD != null && maxSD != null && minSD > maxSD) {
|
|
89
|
+
onWarn?.(
|
|
90
|
+
`minimumSignificantDigits (${minSD}) > maximumSignificantDigits (${maxSD}); swapping.`,
|
|
91
|
+
)
|
|
92
|
+
sanitized.minimumSignificantDigits = maxSD
|
|
93
|
+
sanitized.maximumSignificantDigits = minSD
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const formatter = new Intl.NumberFormat(locale, sanitized)
|
|
98
|
+
if (formatter.resolvedOptions().locale !== locale)
|
|
99
|
+
onWarn(
|
|
100
|
+
`Fall back to locale ${formatter.resolvedOptions().locale} instead of ${locale}`,
|
|
101
|
+
)
|
|
102
|
+
return formatter
|
|
103
|
+
} catch (e) {
|
|
104
|
+
onError?.(
|
|
105
|
+
`Options rejected by Intl.NumberFormat: ${e instanceof Error ? e.message : String(e)}`,
|
|
106
|
+
)
|
|
107
|
+
return useFallback()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default defineComponent<BasicNumberProps>(
|
|
112
|
+
'basic-number',
|
|
113
|
+
{ value: asNumber() },
|
|
114
|
+
undefined,
|
|
115
|
+
({ host }) => {
|
|
116
|
+
const formatter = getNumberFormatter(
|
|
117
|
+
host.closest('[lang]')?.getAttribute('lang') || FALLBACK_LOCALE,
|
|
118
|
+
host.getAttribute('options'),
|
|
119
|
+
)
|
|
120
|
+
return {
|
|
121
|
+
host: setText(() => formatter.format(host.value)),
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<!-- Default example -->
|
|
2
|
+
<basic-pluralize>
|
|
3
|
+
<p class="none">Well done, all done!</p>
|
|
4
|
+
<p class="some">
|
|
5
|
+
<span class="count"></span>
|
|
6
|
+
task<span class="other">s</span>
|
|
7
|
+
remaining
|
|
8
|
+
</p>
|
|
9
|
+
</basic-pluralize>
|
|
10
|
+
|
|
11
|
+
<!-- Test example with plural categories -->
|
|
12
|
+
<basic-pluralize id="plural-test" count="1">
|
|
13
|
+
<p class="none">Nobody</p>
|
|
14
|
+
<p class="some">
|
|
15
|
+
<span class="count"></span>
|
|
16
|
+
<span class="one">person</span>
|
|
17
|
+
<span class="other">people</span>
|
|
18
|
+
</p>
|
|
19
|
+
</basic-pluralize>
|
|
20
|
+
|
|
21
|
+
<!-- Welsh locale example - demonstrates complex pluralization rules
|
|
22
|
+
Welsh has 6 plural categories: zero, one, two, few, many, other
|
|
23
|
+
Example: "dog" in Welsh
|
|
24
|
+
0 = cŵn (zero), 1 = ci (one), 2 = gi (two), 3 = chi (few), 6 = chi (many), 4/5/7+ = ci (other) -->
|
|
25
|
+
<div lang="cy">
|
|
26
|
+
<basic-pluralize id="welsh-test" count="0">
|
|
27
|
+
<p class="none">Dim anifeiliaid!</p>
|
|
28
|
+
<p class="some">
|
|
29
|
+
<span class="count"></span>
|
|
30
|
+
<span class="zero">cŵn</span>
|
|
31
|
+
<span class="one">ci</span>
|
|
32
|
+
<span class="two">gi</span>
|
|
33
|
+
<span class="few">chi</span>
|
|
34
|
+
<span class="many">chi</span>
|
|
35
|
+
<span class="other">ci</span>
|
|
36
|
+
</p>
|
|
37
|
+
</basic-pluralize>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Ordinal example -->
|
|
41
|
+
<basic-pluralize id="ordinal-test" count="1" ordinal>
|
|
42
|
+
<p class="none">None</p>
|
|
43
|
+
<p class="some">
|
|
44
|
+
<span class="count"></span><span class="one">st</span
|
|
45
|
+
><span class="two">nd</span><span class="few">rd</span
|
|
46
|
+
><span class="other">th</span>
|
|
47
|
+
</p>
|
|
48
|
+
</basic-pluralize>
|
|
49
|
+
|
|
50
|
+
<!-- Multiple instances example -->
|
|
51
|
+
<basic-pluralize id="pluralize-1" count="0">
|
|
52
|
+
<p class="none">No items</p>
|
|
53
|
+
<p class="some">
|
|
54
|
+
<span class="count"></span> item<span class="other">s</span>
|
|
55
|
+
</p>
|
|
56
|
+
</basic-pluralize>
|
|
57
|
+
|
|
58
|
+
<basic-pluralize id="pluralize-2" count="5">
|
|
59
|
+
<p class="none">No filters</p>
|
|
60
|
+
<p class="some">
|
|
61
|
+
<span class="count"></span> filter criteri<span class="one">on</span
|
|
62
|
+
><span class="other">a</span>
|
|
63
|
+
</p>
|
|
64
|
+
</basic-pluralize>
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
test.describe('basic-pluralize component', () => {
|
|
4
|
+
test.beforeEach(async ({ page }) => {
|
|
5
|
+
page.on('console', msg => {
|
|
6
|
+
console.log(`[browser] ${msg.type()}: ${msg.text()}`)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
await page.goto('http://localhost:3000/test/basic-pluralize.html')
|
|
10
|
+
await page.waitForSelector('basic-pluralize')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('renders correctly with count=0 (shows none, hides some)', async ({
|
|
14
|
+
page,
|
|
15
|
+
}) => {
|
|
16
|
+
// Set count to 0 on the default element
|
|
17
|
+
await page.evaluate(() => {
|
|
18
|
+
const element = document.querySelector('basic-pluralize') as any
|
|
19
|
+
element.setAttribute('count', '0')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// Should show .none and hide .some when count is 0
|
|
23
|
+
const defaultElement = page.locator('basic-pluralize').first()
|
|
24
|
+
const noneElement = defaultElement.locator('.none')
|
|
25
|
+
const someElement = defaultElement.locator('.some')
|
|
26
|
+
|
|
27
|
+
await expect(noneElement).toBeVisible()
|
|
28
|
+
await expect(someElement).toBeHidden()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('renders correctly with count>0 (hides none, shows some)', async ({
|
|
32
|
+
page,
|
|
33
|
+
}) => {
|
|
34
|
+
// Set count to 5 on the default element
|
|
35
|
+
await page.evaluate(() => {
|
|
36
|
+
const element = document.querySelector('basic-pluralize') as any
|
|
37
|
+
element.setAttribute('count', '5')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const defaultElement = page.locator('basic-pluralize').first()
|
|
41
|
+
const noneElement = defaultElement.locator('.none')
|
|
42
|
+
const someElement = defaultElement.locator('.some')
|
|
43
|
+
const countSpan = defaultElement.locator('.count')
|
|
44
|
+
|
|
45
|
+
await expect(noneElement).toBeHidden()
|
|
46
|
+
await expect(someElement).toBeVisible()
|
|
47
|
+
await expect(countSpan).toHaveText('5')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('updates count display when attribute changes', async ({ page }) => {
|
|
51
|
+
await page.evaluate(() => {
|
|
52
|
+
const element = document.querySelector('basic-pluralize') as any
|
|
53
|
+
element.setAttribute('count', '1')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const defaultElement = page.locator('basic-pluralize').first()
|
|
57
|
+
const countSpan = defaultElement.locator('.count')
|
|
58
|
+
await expect(countSpan).toHaveText('1')
|
|
59
|
+
|
|
60
|
+
// Change to different count
|
|
61
|
+
await page.evaluate(() => {
|
|
62
|
+
const element = document.querySelector('basic-pluralize') as any
|
|
63
|
+
element.setAttribute('count', '42')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
await expect(countSpan).toHaveText('42')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('handles plural categories correctly (English)', async ({ page }) => {
|
|
70
|
+
// Use the existing #plural-test element from HTML
|
|
71
|
+
const testElement = page.locator('#plural-test')
|
|
72
|
+
const oneElement = testElement.locator('.one')
|
|
73
|
+
const otherElement = testElement.locator('.other')
|
|
74
|
+
|
|
75
|
+
// Should start with count=1, showing 'one' category
|
|
76
|
+
await expect(oneElement).toBeVisible()
|
|
77
|
+
await expect(otherElement).toBeHidden()
|
|
78
|
+
|
|
79
|
+
// Test count = 2 (should show 'other' category)
|
|
80
|
+
await page.evaluate(() => {
|
|
81
|
+
const element = document.querySelector('#plural-test') as any
|
|
82
|
+
element.setAttribute('count', '2')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await expect(oneElement).toBeHidden()
|
|
86
|
+
await expect(otherElement).toBeVisible()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('handles Welsh pluralization with all 6 categories (zero, one, two, few, many, other)', async ({
|
|
90
|
+
page,
|
|
91
|
+
}) => {
|
|
92
|
+
// Use the existing Welsh example from HTML
|
|
93
|
+
const welshElement = page.locator('#welsh-test')
|
|
94
|
+
const countSpan = welshElement.locator('.count')
|
|
95
|
+
|
|
96
|
+
// Should start with count=0, showing zero form "cŵn"
|
|
97
|
+
await expect(countSpan).toHaveText('0')
|
|
98
|
+
await expect(welshElement.locator('.none')).toBeVisible()
|
|
99
|
+
await expect(welshElement.locator('.some')).toBeHidden()
|
|
100
|
+
|
|
101
|
+
// Test count=1 (should show "ci" - one form)
|
|
102
|
+
await page.evaluate(() => {
|
|
103
|
+
const element = document.querySelector('#welsh-test') as any
|
|
104
|
+
element.setAttribute('count', '1')
|
|
105
|
+
})
|
|
106
|
+
await expect(welshElement.locator('.none')).toBeHidden()
|
|
107
|
+
await expect(welshElement.locator('.some')).toBeVisible()
|
|
108
|
+
await expect(welshElement.locator('.one')).toBeVisible()
|
|
109
|
+
await expect(welshElement.locator('.other')).toBeHidden()
|
|
110
|
+
|
|
111
|
+
// Test count=2 (should show "gi" - two form)
|
|
112
|
+
await page.evaluate(() => {
|
|
113
|
+
const element = document.querySelector('#welsh-test') as any
|
|
114
|
+
element.setAttribute('count', '2')
|
|
115
|
+
})
|
|
116
|
+
await expect(welshElement.locator('.one')).toBeHidden()
|
|
117
|
+
await expect(welshElement.locator('.two')).toBeVisible()
|
|
118
|
+
await expect(welshElement.locator('.other')).toBeHidden()
|
|
119
|
+
|
|
120
|
+
// Test count=3 (should show "chi" - few form)
|
|
121
|
+
await page.evaluate(() => {
|
|
122
|
+
const element = document.querySelector('#welsh-test') as any
|
|
123
|
+
element.setAttribute('count', '3')
|
|
124
|
+
})
|
|
125
|
+
await expect(welshElement.locator('.two')).toBeHidden()
|
|
126
|
+
await expect(welshElement.locator('.few')).toBeVisible()
|
|
127
|
+
await expect(welshElement.locator('.other')).toBeHidden()
|
|
128
|
+
|
|
129
|
+
// Test count=6 (should show "chi" - many form)
|
|
130
|
+
await page.evaluate(() => {
|
|
131
|
+
const element = document.querySelector('#welsh-test') as any
|
|
132
|
+
element.setAttribute('count', '6')
|
|
133
|
+
})
|
|
134
|
+
await expect(welshElement.locator('.few')).toBeHidden()
|
|
135
|
+
await expect(welshElement.locator('.many')).toBeVisible()
|
|
136
|
+
await expect(welshElement.locator('.other')).toBeHidden()
|
|
137
|
+
|
|
138
|
+
// Test count=4 (should show "ci" - other form)
|
|
139
|
+
await page.evaluate(() => {
|
|
140
|
+
const element = document.querySelector('#welsh-test') as any
|
|
141
|
+
element.setAttribute('count', '4')
|
|
142
|
+
})
|
|
143
|
+
await expect(welshElement.locator('.many')).toBeHidden()
|
|
144
|
+
await expect(welshElement.locator('.other')).toBeVisible()
|
|
145
|
+
await expect(welshElement.locator('.few')).toBeHidden()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('handles ordinal attribute correctly', async ({ page }) => {
|
|
149
|
+
// Use the existing #ordinal-test element from HTML
|
|
150
|
+
const ordinalElement = page.locator('#ordinal-test')
|
|
151
|
+
const countSpan = ordinalElement.locator('.count')
|
|
152
|
+
|
|
153
|
+
// Should start with count=1, showing "st" (first)
|
|
154
|
+
await expect(countSpan).toHaveText('1')
|
|
155
|
+
await expect(ordinalElement.locator('.some')).toBeVisible()
|
|
156
|
+
await expect(ordinalElement.locator('.none')).toBeHidden()
|
|
157
|
+
await expect(ordinalElement.locator('.one')).toBeVisible()
|
|
158
|
+
await expect(ordinalElement.locator('.other')).toBeHidden()
|
|
159
|
+
|
|
160
|
+
// Test count=2 (should show "nd" - second)
|
|
161
|
+
await page.evaluate(() => {
|
|
162
|
+
const element = document.querySelector('#ordinal-test') as any
|
|
163
|
+
element.setAttribute('count', '2')
|
|
164
|
+
})
|
|
165
|
+
await expect(countSpan).toHaveText('2')
|
|
166
|
+
await expect(ordinalElement.locator('.one')).toBeHidden()
|
|
167
|
+
await expect(ordinalElement.locator('.two')).toBeVisible()
|
|
168
|
+
await expect(ordinalElement.locator('.other')).toBeHidden()
|
|
169
|
+
|
|
170
|
+
// Test count=3 (should show "rd" - third)
|
|
171
|
+
await page.evaluate(() => {
|
|
172
|
+
const element = document.querySelector('#ordinal-test') as any
|
|
173
|
+
element.setAttribute('count', '3')
|
|
174
|
+
})
|
|
175
|
+
await expect(countSpan).toHaveText('3')
|
|
176
|
+
await expect(ordinalElement.locator('.two')).toBeHidden()
|
|
177
|
+
await expect(ordinalElement.locator('.few')).toBeVisible()
|
|
178
|
+
await expect(ordinalElement.locator('.other')).toBeHidden()
|
|
179
|
+
|
|
180
|
+
// Test count=4 (should show "th" - other)
|
|
181
|
+
await page.evaluate(() => {
|
|
182
|
+
const element = document.querySelector('#ordinal-test') as any
|
|
183
|
+
element.setAttribute('count', '4')
|
|
184
|
+
})
|
|
185
|
+
await expect(countSpan).toHaveText('4')
|
|
186
|
+
await expect(ordinalElement.locator('.few')).toBeHidden()
|
|
187
|
+
await expect(ordinalElement.locator('.other')).toBeVisible()
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test('handles multiple instances with different counts', async ({ page }) => {
|
|
191
|
+
// Use the existing elements from HTML
|
|
192
|
+
const firstElement = page.locator('#pluralize-1')
|
|
193
|
+
const secondElement = page.locator('#pluralize-2')
|
|
194
|
+
|
|
195
|
+
// First element (count=0) - should show "No items"
|
|
196
|
+
await expect(firstElement.locator('.none')).toBeVisible()
|
|
197
|
+
await expect(firstElement.locator('.some')).toBeHidden()
|
|
198
|
+
|
|
199
|
+
// Second element (count=5) - should show "5 filter criteria" (other form)
|
|
200
|
+
await expect(secondElement.locator('.none')).toBeHidden()
|
|
201
|
+
await expect(secondElement.locator('.some')).toBeVisible()
|
|
202
|
+
await expect(secondElement.locator('.count')).toHaveText('5')
|
|
203
|
+
await expect(secondElement.locator('.other')).toBeVisible()
|
|
204
|
+
await expect(secondElement.locator('.one')).toBeHidden()
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('handles large counts correctly', async ({ page }) => {
|
|
208
|
+
await page.evaluate(() => {
|
|
209
|
+
const element = document.querySelector('basic-pluralize') as any
|
|
210
|
+
element.setAttribute('count', '1000')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
const defaultElement = page.locator('basic-pluralize').first()
|
|
214
|
+
const noneElement = defaultElement.locator('.none')
|
|
215
|
+
const someElement = defaultElement.locator('.some')
|
|
216
|
+
const countSpan = defaultElement.locator('.count')
|
|
217
|
+
|
|
218
|
+
// Large count should show .some and hide .none
|
|
219
|
+
await expect(noneElement).toBeHidden()
|
|
220
|
+
await expect(someElement).toBeVisible()
|
|
221
|
+
await expect(countSpan).toHaveText('1000')
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
test('handles negative counts by falling back to 0', async ({ page }) => {
|
|
225
|
+
await page.evaluate(() => {
|
|
226
|
+
const element = document.querySelector('basic-pluralize') as any
|
|
227
|
+
element.setAttribute('count', '-5')
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const defaultElement = page.locator('basic-pluralize').first()
|
|
231
|
+
const noneElement = defaultElement.locator('.none')
|
|
232
|
+
const someElement = defaultElement.locator('.some')
|
|
233
|
+
const countSpan = defaultElement.locator('.count')
|
|
234
|
+
|
|
235
|
+
// Negative count should be treated as 0: .none shows, .some hides
|
|
236
|
+
await expect(noneElement).toBeVisible()
|
|
237
|
+
await expect(someElement).toBeHidden()
|
|
238
|
+
await expect(countSpan).toHaveText('0')
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
test('handles non-numeric count gracefully', async ({ page }) => {
|
|
242
|
+
// Test with invalid count - asPositiveInteger should default to 0
|
|
243
|
+
await page.evaluate(() => {
|
|
244
|
+
const element = document.querySelector('basic-pluralize') as any
|
|
245
|
+
element.setAttribute('count', 'invalid')
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const defaultElement = page.locator('basic-pluralize').first()
|
|
249
|
+
const noneElement = defaultElement.locator('.none')
|
|
250
|
+
const someElement = defaultElement.locator('.some')
|
|
251
|
+
const countSpan = defaultElement.locator('.count')
|
|
252
|
+
|
|
253
|
+
// Should default to 0, showing .none
|
|
254
|
+
await expect(noneElement).toBeVisible()
|
|
255
|
+
await expect(someElement).toBeHidden()
|
|
256
|
+
await expect(countSpan).toHaveText('0')
|
|
257
|
+
})
|
|
258
|
+
})
|