@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,688 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
test.describe('form-spinbutton 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/form-spinbutton.html')
|
|
10
|
+
await page.waitForSelector('form-spinbutton')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('renders initial state correctly', async ({ page }) => {
|
|
14
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
15
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
16
|
+
const decrementButton = spinbutton.locator('button.decrement')
|
|
17
|
+
const input = spinbutton.locator('input.value')
|
|
18
|
+
const zeroElement = spinbutton.locator('.zero')
|
|
19
|
+
const otherElement = spinbutton.locator('.other')
|
|
20
|
+
|
|
21
|
+
// Initial value should be 0
|
|
22
|
+
await expect(input).toHaveValue('0')
|
|
23
|
+
await expect(input).toBeHidden()
|
|
24
|
+
|
|
25
|
+
// Decrement button should be hidden when value is 0
|
|
26
|
+
await expect(decrementButton).toBeHidden()
|
|
27
|
+
|
|
28
|
+
// Increment button should be enabled and visible
|
|
29
|
+
await expect(incrementButton).toBeVisible()
|
|
30
|
+
await expect(incrementButton).not.toHaveAttribute('disabled')
|
|
31
|
+
|
|
32
|
+
// Zero element should be visible, other element hidden
|
|
33
|
+
await expect(zeroElement).toBeVisible()
|
|
34
|
+
await expect(zeroElement).toHaveText('Add to Cart')
|
|
35
|
+
await expect(otherElement).toBeHidden()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('increments value when clicking increment button', async ({ page }) => {
|
|
39
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
40
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
41
|
+
const input = spinbutton.locator('input.value')
|
|
42
|
+
const decrementButton = spinbutton.locator('button.decrement')
|
|
43
|
+
const zeroElement = spinbutton.locator('.zero')
|
|
44
|
+
const otherElement = spinbutton.locator('.other')
|
|
45
|
+
|
|
46
|
+
// Click increment button
|
|
47
|
+
await incrementButton.click()
|
|
48
|
+
|
|
49
|
+
// Value should be 1 and visible
|
|
50
|
+
await expect(input).toHaveValue('1')
|
|
51
|
+
await expect(input).toBeVisible()
|
|
52
|
+
|
|
53
|
+
// Decrement button should now be visible
|
|
54
|
+
await expect(decrementButton).toBeVisible()
|
|
55
|
+
|
|
56
|
+
// Zero element should be hidden, other element visible
|
|
57
|
+
await expect(zeroElement).toBeHidden()
|
|
58
|
+
await expect(otherElement).toBeVisible()
|
|
59
|
+
|
|
60
|
+
// Click increment again
|
|
61
|
+
await incrementButton.click()
|
|
62
|
+
await expect(input).toHaveValue('2')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('decrements value when clicking decrement button', async ({ page }) => {
|
|
66
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
67
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
68
|
+
const decrementButton = spinbutton.locator('button.decrement')
|
|
69
|
+
const input = spinbutton.locator('input.value')
|
|
70
|
+
const zeroElement = spinbutton.locator('.zero')
|
|
71
|
+
const otherElement = spinbutton.locator('.other')
|
|
72
|
+
|
|
73
|
+
// First increment to 2
|
|
74
|
+
await incrementButton.click()
|
|
75
|
+
await incrementButton.click()
|
|
76
|
+
await expect(input).toHaveValue('2')
|
|
77
|
+
|
|
78
|
+
// Then decrement
|
|
79
|
+
await decrementButton.click()
|
|
80
|
+
await expect(input).toHaveValue('1')
|
|
81
|
+
await expect(decrementButton).toBeVisible()
|
|
82
|
+
await expect(otherElement).toBeVisible()
|
|
83
|
+
|
|
84
|
+
// Decrement to 0
|
|
85
|
+
await decrementButton.click()
|
|
86
|
+
await expect(input).toHaveValue('0')
|
|
87
|
+
await expect(input).toBeHidden()
|
|
88
|
+
await expect(decrementButton).toBeHidden()
|
|
89
|
+
await expect(zeroElement).toBeVisible()
|
|
90
|
+
await expect(otherElement).toBeHidden()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('respects max value constraint', async ({ page }) => {
|
|
94
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
95
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
96
|
+
const input = spinbutton.locator('input.value')
|
|
97
|
+
|
|
98
|
+
// Click increment 10 times to reach max (10)
|
|
99
|
+
for (let i = 0; i < 10; i++) {
|
|
100
|
+
await incrementButton.click()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await expect(input).toHaveValue('10')
|
|
104
|
+
await expect(incrementButton).toHaveAttribute('disabled')
|
|
105
|
+
|
|
106
|
+
// Button should be disabled at max, value should stay at 10
|
|
107
|
+
await expect(input).toHaveValue('10')
|
|
108
|
+
await expect(incrementButton).toHaveAttribute('disabled')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('handles keyboard interactions on buttons', async ({ page }) => {
|
|
112
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
113
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
114
|
+
const input = spinbutton.locator('input.value')
|
|
115
|
+
|
|
116
|
+
// Focus a button (keyboard events are handled on controls collection)
|
|
117
|
+
await incrementButton.focus()
|
|
118
|
+
|
|
119
|
+
// Test ArrowUp
|
|
120
|
+
await page.keyboard.press('ArrowUp')
|
|
121
|
+
await expect(input).toHaveValue('1')
|
|
122
|
+
await expect(input).toBeVisible()
|
|
123
|
+
|
|
124
|
+
// Test ArrowUp again
|
|
125
|
+
await page.keyboard.press('ArrowUp')
|
|
126
|
+
await expect(input).toHaveValue('2')
|
|
127
|
+
|
|
128
|
+
// Test ArrowDown
|
|
129
|
+
await page.keyboard.press('ArrowDown')
|
|
130
|
+
await expect(input).toHaveValue('1')
|
|
131
|
+
|
|
132
|
+
// Test + key
|
|
133
|
+
await page.keyboard.press('+')
|
|
134
|
+
await expect(input).toHaveValue('2')
|
|
135
|
+
|
|
136
|
+
// Test - key
|
|
137
|
+
await page.keyboard.press('-')
|
|
138
|
+
await expect(input).toHaveValue('1')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test('handles keyboard interactions on input when enabled', async ({
|
|
142
|
+
page,
|
|
143
|
+
}) => {
|
|
144
|
+
// Use the interactive-input-test which has an input that's not disabled
|
|
145
|
+
const spinbutton = page.locator('#interactive-input-test')
|
|
146
|
+
const input = spinbutton.locator('input.value')
|
|
147
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
148
|
+
|
|
149
|
+
// First make input visible by incrementing
|
|
150
|
+
await incrementButton.click()
|
|
151
|
+
await expect(input).toBeVisible()
|
|
152
|
+
|
|
153
|
+
// Focus the input directly
|
|
154
|
+
await input.focus()
|
|
155
|
+
|
|
156
|
+
// Test ArrowUp
|
|
157
|
+
await page.keyboard.press('ArrowUp')
|
|
158
|
+
await expect(input).toHaveValue('2')
|
|
159
|
+
|
|
160
|
+
// Test ArrowDown
|
|
161
|
+
await page.keyboard.press('ArrowDown')
|
|
162
|
+
await expect(input).toHaveValue('1')
|
|
163
|
+
|
|
164
|
+
// Test + key
|
|
165
|
+
await page.keyboard.press('+')
|
|
166
|
+
await expect(input).toHaveValue('2')
|
|
167
|
+
|
|
168
|
+
// Test - key
|
|
169
|
+
await page.keyboard.press('-')
|
|
170
|
+
await expect(input).toHaveValue('1')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('handles direct input value changes with validation', async ({
|
|
174
|
+
page,
|
|
175
|
+
}) => {
|
|
176
|
+
// Use the interactive-input-test which has an input that's not disabled
|
|
177
|
+
const spinbutton = page.locator('#interactive-input-test')
|
|
178
|
+
const input = spinbutton.locator('input.value')
|
|
179
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
180
|
+
|
|
181
|
+
// First make input visible by incrementing
|
|
182
|
+
await incrementButton.click()
|
|
183
|
+
await expect(input).toBeVisible()
|
|
184
|
+
|
|
185
|
+
// Clear and type a valid value
|
|
186
|
+
await input.fill('3')
|
|
187
|
+
await input.blur() // Trigger change event
|
|
188
|
+
await expect(input).toHaveValue('3')
|
|
189
|
+
|
|
190
|
+
// Try to input a value above max (should be clamped)
|
|
191
|
+
await input.fill('15')
|
|
192
|
+
await input.blur()
|
|
193
|
+
await expect(input).toHaveValue('12') // Should be clamped to max
|
|
194
|
+
|
|
195
|
+
// Try to input a negative value (should be clamped to 0, which hides input)
|
|
196
|
+
await input.fill('-5')
|
|
197
|
+
await input.blur()
|
|
198
|
+
await expect(input).toHaveValue('0')
|
|
199
|
+
await expect(input).toBeHidden()
|
|
200
|
+
|
|
201
|
+
// Make input visible again for next test
|
|
202
|
+
await incrementButton.click()
|
|
203
|
+
await incrementButton.click()
|
|
204
|
+
await expect(input).toBeVisible()
|
|
205
|
+
await expect(input).toHaveValue('2')
|
|
206
|
+
|
|
207
|
+
// Try to input a non-integer (should reset to previous valid value)
|
|
208
|
+
await input.fill('2.5')
|
|
209
|
+
await input.blur()
|
|
210
|
+
await expect(input).toHaveValue('2') // Should reset to previous valid value
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('keyboard interactions respect constraints', async ({ page }) => {
|
|
214
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
215
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
216
|
+
const input = spinbutton.locator('input.value')
|
|
217
|
+
|
|
218
|
+
await incrementButton.focus()
|
|
219
|
+
|
|
220
|
+
// Go to max value using keyboard
|
|
221
|
+
for (let i = 0; i < 10; i++) {
|
|
222
|
+
await page.keyboard.press('ArrowUp')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
await expect(input).toHaveValue('10')
|
|
226
|
+
await expect(incrementButton).toHaveAttribute('disabled')
|
|
227
|
+
|
|
228
|
+
// Try to go past max
|
|
229
|
+
await page.keyboard.press('ArrowUp')
|
|
230
|
+
await expect(input).toHaveValue('10')
|
|
231
|
+
|
|
232
|
+
// Switch to decrement button to go down
|
|
233
|
+
const decrementButton = spinbutton.locator('button.decrement')
|
|
234
|
+
await decrementButton.focus()
|
|
235
|
+
|
|
236
|
+
// Go down to 0 and try to go below
|
|
237
|
+
for (let i = 0; i < 10; i++) {
|
|
238
|
+
await page.keyboard.press('ArrowDown')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await expect(input).toHaveValue('0')
|
|
242
|
+
await expect(input).toBeHidden()
|
|
243
|
+
|
|
244
|
+
// Try to go below 0
|
|
245
|
+
await page.keyboard.press('ArrowDown')
|
|
246
|
+
await expect(input).toHaveValue('0')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('keyboard events are prevented from propagating', async ({ page }) => {
|
|
250
|
+
// Set up event listener on document to detect if events bubble up
|
|
251
|
+
await page.evaluate(() => {
|
|
252
|
+
;(window as any).documentKeydownCount = 0
|
|
253
|
+
document.addEventListener('keydown', e => {
|
|
254
|
+
if (['ArrowUp', 'ArrowDown', '+', '-'].includes(e.key)) {
|
|
255
|
+
;(window as any).documentKeydownCount++
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
261
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
262
|
+
await incrementButton.focus()
|
|
263
|
+
|
|
264
|
+
// Press handled keys
|
|
265
|
+
await page.keyboard.press('ArrowUp')
|
|
266
|
+
await page.keyboard.press('ArrowDown')
|
|
267
|
+
await page.keyboard.press('+')
|
|
268
|
+
await page.keyboard.press('-')
|
|
269
|
+
|
|
270
|
+
// Check that events were prevented from reaching document
|
|
271
|
+
const documentKeydownCount = await page.evaluate(
|
|
272
|
+
() => (window as any).documentKeydownCount,
|
|
273
|
+
)
|
|
274
|
+
expect(documentKeydownCount).toBe(0)
|
|
275
|
+
|
|
276
|
+
// Test that other keys still propagate
|
|
277
|
+
await page.keyboard.press('Escape')
|
|
278
|
+
await page.keyboard.press('Tab')
|
|
279
|
+
|
|
280
|
+
// These should have propagated (but we don't count them in our listener)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('syncs value property with DOM interactions', async ({ page }) => {
|
|
284
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
285
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
286
|
+
|
|
287
|
+
// Initial value should be 0
|
|
288
|
+
let valueProperty = await page.evaluate(() => {
|
|
289
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
290
|
+
return element.value
|
|
291
|
+
})
|
|
292
|
+
expect(valueProperty).toBe(0)
|
|
293
|
+
|
|
294
|
+
// Click increment button
|
|
295
|
+
await incrementButton.click()
|
|
296
|
+
|
|
297
|
+
valueProperty = await page.evaluate(() => {
|
|
298
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
299
|
+
return element.value
|
|
300
|
+
})
|
|
301
|
+
expect(valueProperty).toBe(1)
|
|
302
|
+
|
|
303
|
+
// Use keyboard to increment
|
|
304
|
+
await incrementButton.focus()
|
|
305
|
+
await page.keyboard.press('ArrowUp')
|
|
306
|
+
|
|
307
|
+
valueProperty = await page.evaluate(() => {
|
|
308
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
309
|
+
return element.value
|
|
310
|
+
})
|
|
311
|
+
expect(valueProperty).toBe(2)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('reads max value from input max attribute', async ({ page }) => {
|
|
315
|
+
// Check that max property reads from input.max
|
|
316
|
+
const maxProperty = await page.evaluate(() => {
|
|
317
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
318
|
+
return element.max
|
|
319
|
+
})
|
|
320
|
+
expect(maxProperty).toBe(10)
|
|
321
|
+
|
|
322
|
+
// Test with another component that has different max
|
|
323
|
+
const max5Property = await page.evaluate(() => {
|
|
324
|
+
const element = document.querySelector('#max-5-test') as any
|
|
325
|
+
return element.max
|
|
326
|
+
})
|
|
327
|
+
expect(max5Property).toBe(5)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
test('handles aria-label updates correctly', async ({ page }) => {
|
|
331
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
332
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
333
|
+
|
|
334
|
+
// When value is 0, aria-label should use zero element text
|
|
335
|
+
let ariaLabel = await incrementButton.getAttribute('aria-label')
|
|
336
|
+
expect(ariaLabel).toBe('Add to Cart')
|
|
337
|
+
|
|
338
|
+
// When value is > 0, should use original aria-label
|
|
339
|
+
await incrementButton.click()
|
|
340
|
+
ariaLabel = await incrementButton.getAttribute('aria-label')
|
|
341
|
+
expect(ariaLabel).toBe('Increment')
|
|
342
|
+
|
|
343
|
+
// When back to 0, should use zero element text again
|
|
344
|
+
const decrementButton = spinbutton.locator('button.decrement')
|
|
345
|
+
await decrementButton.click()
|
|
346
|
+
ariaLabel = await incrementButton.getAttribute('aria-label')
|
|
347
|
+
expect(ariaLabel).toBe('Add to Cart')
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
test('value property is readonly (sensor-based)', async ({ page }) => {
|
|
351
|
+
// Test that the value property reflects DOM state but can't be controlled externally
|
|
352
|
+
const initialValue = await page.evaluate(() => {
|
|
353
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
354
|
+
return element.value
|
|
355
|
+
})
|
|
356
|
+
expect(initialValue).toBe(0)
|
|
357
|
+
|
|
358
|
+
// Click the increment button
|
|
359
|
+
const incrementButton = page
|
|
360
|
+
.locator('form-spinbutton button.increment')
|
|
361
|
+
.first()
|
|
362
|
+
await incrementButton.click()
|
|
363
|
+
|
|
364
|
+
// Property should now reflect the updated state
|
|
365
|
+
const valueAfterClick = await page.evaluate(() => {
|
|
366
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
367
|
+
return element.value
|
|
368
|
+
})
|
|
369
|
+
expect(valueAfterClick).toBe(1)
|
|
370
|
+
|
|
371
|
+
// Verify that trying to set the value property directly doesn't work
|
|
372
|
+
// (since it's a readonly sensor)
|
|
373
|
+
await page.evaluate(() => {
|
|
374
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
375
|
+
try {
|
|
376
|
+
element.value = 5
|
|
377
|
+
} catch (_e) {
|
|
378
|
+
// Expected - property might be readonly
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
// Value should still be 1 (not changed to 5)
|
|
383
|
+
const input = page.locator('form-spinbutton input.value').first()
|
|
384
|
+
await expect(input).toHaveValue('1')
|
|
385
|
+
|
|
386
|
+
// The property should still reflect the DOM state
|
|
387
|
+
const valueAfterAttemptedChange = await page.evaluate(() => {
|
|
388
|
+
const element = document.querySelector('form-spinbutton') as any
|
|
389
|
+
return element.value
|
|
390
|
+
})
|
|
391
|
+
expect(valueAfterAttemptedChange).toBe(1)
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
test('handles rapid button clicks correctly', async ({ page }) => {
|
|
395
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
396
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
397
|
+
const decrementButton = spinbutton.locator('button.decrement')
|
|
398
|
+
const input = spinbutton.locator('input.value')
|
|
399
|
+
|
|
400
|
+
// Rapid increment clicks
|
|
401
|
+
await incrementButton.click()
|
|
402
|
+
await incrementButton.click()
|
|
403
|
+
await incrementButton.click()
|
|
404
|
+
await expect(input).toHaveValue('3')
|
|
405
|
+
|
|
406
|
+
// Rapid decrement clicks
|
|
407
|
+
await decrementButton.click()
|
|
408
|
+
await decrementButton.click()
|
|
409
|
+
await expect(input).toHaveValue('1')
|
|
410
|
+
|
|
411
|
+
// Mix of rapid clicks
|
|
412
|
+
await incrementButton.click()
|
|
413
|
+
await decrementButton.click()
|
|
414
|
+
await incrementButton.click()
|
|
415
|
+
await expect(input).toHaveValue('2')
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
test('maintains UI state consistency during value changes', async ({
|
|
419
|
+
page,
|
|
420
|
+
}) => {
|
|
421
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
422
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
423
|
+
const decrementButton = spinbutton.locator('button.decrement')
|
|
424
|
+
const input = spinbutton.locator('input.value')
|
|
425
|
+
const zeroElement = spinbutton.locator('.zero')
|
|
426
|
+
const otherElement = spinbutton.locator('.other')
|
|
427
|
+
|
|
428
|
+
// Test transition from 0 to 1
|
|
429
|
+
await incrementButton.click()
|
|
430
|
+
await expect(input).toBeVisible()
|
|
431
|
+
await expect(decrementButton).toBeVisible()
|
|
432
|
+
await expect(zeroElement).toBeHidden()
|
|
433
|
+
await expect(otherElement).toBeVisible()
|
|
434
|
+
|
|
435
|
+
// Test transition from 1 to 0
|
|
436
|
+
await decrementButton.click()
|
|
437
|
+
await expect(input).toBeHidden()
|
|
438
|
+
await expect(decrementButton).toBeHidden()
|
|
439
|
+
await expect(zeroElement).toBeVisible()
|
|
440
|
+
await expect(otherElement).toBeHidden()
|
|
441
|
+
|
|
442
|
+
// Test reaching max value
|
|
443
|
+
for (let i = 0; i < 10; i++) {
|
|
444
|
+
await incrementButton.click()
|
|
445
|
+
}
|
|
446
|
+
await expect(incrementButton).toHaveAttribute('disabled')
|
|
447
|
+
await expect(input).toHaveValue('10')
|
|
448
|
+
await expect(decrementButton).toBeVisible()
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
test('handles focus and keyboard navigation', async ({ page }) => {
|
|
452
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
453
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
454
|
+
const input = spinbutton.locator('input.value')
|
|
455
|
+
|
|
456
|
+
// Button should be focusable
|
|
457
|
+
await incrementButton.focus()
|
|
458
|
+
await expect(incrementButton).toBeFocused()
|
|
459
|
+
|
|
460
|
+
// Test that we can use keyboard while focused
|
|
461
|
+
await page.keyboard.press('ArrowUp')
|
|
462
|
+
await expect(input).toHaveValue('1')
|
|
463
|
+
|
|
464
|
+
// Test refocusing the button and using keyboard again
|
|
465
|
+
await incrementButton.focus()
|
|
466
|
+
await expect(incrementButton).toBeFocused()
|
|
467
|
+
|
|
468
|
+
// Should still be able to use keyboard after refocus
|
|
469
|
+
await page.keyboard.press('ArrowUp')
|
|
470
|
+
await expect(input).toHaveValue('2')
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
test('reads initial value from DOM content', async ({ page }) => {
|
|
474
|
+
// Test component that has initial value set in DOM
|
|
475
|
+
const initialValueSpinbutton = page.locator('#initial-value-test')
|
|
476
|
+
const input = initialValueSpinbutton.locator('input.value')
|
|
477
|
+
const incrementButton = initialValueSpinbutton.locator('button.increment')
|
|
478
|
+
const decrementButton = initialValueSpinbutton.locator('button.decrement')
|
|
479
|
+
const otherElement = initialValueSpinbutton.locator('.other')
|
|
480
|
+
|
|
481
|
+
// Should read initial value from DOM
|
|
482
|
+
await expect(input).toHaveValue('3')
|
|
483
|
+
await expect(input).toBeVisible()
|
|
484
|
+
|
|
485
|
+
// UI should reflect non-zero state
|
|
486
|
+
await expect(decrementButton).toBeVisible()
|
|
487
|
+
await expect(otherElement).toBeVisible()
|
|
488
|
+
|
|
489
|
+
// Verify the component property matches
|
|
490
|
+
const valueProperty = await page.evaluate(() => {
|
|
491
|
+
const element = document.querySelector('#initial-value-test') as any
|
|
492
|
+
return element.value
|
|
493
|
+
})
|
|
494
|
+
expect(valueProperty).toBe(3)
|
|
495
|
+
|
|
496
|
+
// Should be able to increment from initial value
|
|
497
|
+
await incrementButton.click()
|
|
498
|
+
await expect(input).toHaveValue('4')
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
test('handles different max values correctly', async ({ page }) => {
|
|
502
|
+
const max5Spinbutton = page.locator('#max-5-test')
|
|
503
|
+
const incrementButton = max5Spinbutton.locator('button.increment')
|
|
504
|
+
const input = max5Spinbutton.locator('input.value')
|
|
505
|
+
|
|
506
|
+
// Increment to max (5)
|
|
507
|
+
for (let i = 0; i < 5; i++) {
|
|
508
|
+
await incrementButton.click()
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
await expect(input).toHaveValue('5')
|
|
512
|
+
await expect(incrementButton).toHaveAttribute('disabled')
|
|
513
|
+
|
|
514
|
+
// Should be disabled at max value
|
|
515
|
+
await expect(incrementButton).toHaveAttribute('disabled')
|
|
516
|
+
await expect(input).toHaveValue('5')
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
test('handles component without zero element', async ({ page }) => {
|
|
520
|
+
const noZeroSpinbutton = page.locator('#no-zero-test')
|
|
521
|
+
const incrementButton = noZeroSpinbutton.locator('button.increment')
|
|
522
|
+
const zeroElement = noZeroSpinbutton.locator('.zero')
|
|
523
|
+
|
|
524
|
+
// Should not have zero element
|
|
525
|
+
await expect(zeroElement).toHaveCount(0)
|
|
526
|
+
|
|
527
|
+
// Aria-label should fallback to original when no zero element exists
|
|
528
|
+
let ariaLabel = await incrementButton.getAttribute('aria-label')
|
|
529
|
+
expect(ariaLabel).toBe('Increment')
|
|
530
|
+
|
|
531
|
+
// After incrementing, should still use original aria-label
|
|
532
|
+
await incrementButton.click()
|
|
533
|
+
ariaLabel = await incrementButton.getAttribute('aria-label')
|
|
534
|
+
expect(ariaLabel).toBe('Increment')
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
test('handles multiple instances independently', async ({ page }) => {
|
|
538
|
+
const defaultSpinbutton = page.locator('form-spinbutton').first()
|
|
539
|
+
const max5Spinbutton = page.locator('#max-5-test')
|
|
540
|
+
const initialValueSpinbutton = page.locator('#initial-value-test')
|
|
541
|
+
|
|
542
|
+
const defaultIncrement = defaultSpinbutton.locator('button.increment')
|
|
543
|
+
const max5Increment = max5Spinbutton.locator('button.increment')
|
|
544
|
+
const initialIncrement = initialValueSpinbutton.locator('button.increment')
|
|
545
|
+
|
|
546
|
+
const defaultInput = defaultSpinbutton.locator('input.value')
|
|
547
|
+
const max5Input = max5Spinbutton.locator('input.value')
|
|
548
|
+
const initialInput = initialValueSpinbutton.locator('input.value')
|
|
549
|
+
|
|
550
|
+
// Verify initial states are different
|
|
551
|
+
await expect(defaultInput).toHaveValue('0')
|
|
552
|
+
await expect(max5Input).toHaveValue('0')
|
|
553
|
+
await expect(initialInput).toHaveValue('3')
|
|
554
|
+
|
|
555
|
+
// Interact with each independently
|
|
556
|
+
await defaultIncrement.click()
|
|
557
|
+
await expect(defaultInput).toHaveValue('1')
|
|
558
|
+
await expect(max5Input).toHaveValue('0')
|
|
559
|
+
await expect(initialInput).toHaveValue('3')
|
|
560
|
+
|
|
561
|
+
await max5Increment.click()
|
|
562
|
+
await max5Increment.click()
|
|
563
|
+
await expect(defaultInput).toHaveValue('1')
|
|
564
|
+
await expect(max5Input).toHaveValue('2')
|
|
565
|
+
await expect(initialInput).toHaveValue('3')
|
|
566
|
+
|
|
567
|
+
await initialIncrement.click()
|
|
568
|
+
await expect(defaultInput).toHaveValue('1')
|
|
569
|
+
await expect(max5Input).toHaveValue('2')
|
|
570
|
+
await expect(initialInput).toHaveValue('4')
|
|
571
|
+
|
|
572
|
+
// Test different max constraints
|
|
573
|
+
// Default max is 10, max5 is 5
|
|
574
|
+
for (let i = 0; i < 3; i++) {
|
|
575
|
+
await max5Increment.click()
|
|
576
|
+
}
|
|
577
|
+
// max5 should be at max (5) and disabled
|
|
578
|
+
await expect(max5Input).toHaveValue('5')
|
|
579
|
+
await expect(max5Increment).toHaveAttribute('disabled')
|
|
580
|
+
|
|
581
|
+
// Default should still be able to increment
|
|
582
|
+
await expect(defaultIncrement).not.toHaveAttribute('disabled')
|
|
583
|
+
await defaultIncrement.click()
|
|
584
|
+
await expect(defaultInput).toHaveValue('2')
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
test('keyboard navigation works across multiple instances', async ({
|
|
588
|
+
page,
|
|
589
|
+
}) => {
|
|
590
|
+
const defaultSpinbutton = page.locator('form-spinbutton').first()
|
|
591
|
+
const max5Spinbutton = page.locator('#max-5-test')
|
|
592
|
+
|
|
593
|
+
const defaultIncrement = defaultSpinbutton.locator('button.increment')
|
|
594
|
+
const max5Increment = max5Spinbutton.locator('button.increment')
|
|
595
|
+
|
|
596
|
+
const defaultInput = defaultSpinbutton.locator('input.value')
|
|
597
|
+
const max5Input = max5Spinbutton.locator('input.value')
|
|
598
|
+
|
|
599
|
+
// Focus first instance and use keyboard
|
|
600
|
+
await defaultIncrement.focus()
|
|
601
|
+
await page.keyboard.press('ArrowUp')
|
|
602
|
+
await expect(defaultInput).toHaveValue('1')
|
|
603
|
+
await expect(max5Input).toHaveValue('0')
|
|
604
|
+
|
|
605
|
+
// Focus second instance and use keyboard
|
|
606
|
+
await max5Increment.focus()
|
|
607
|
+
await page.keyboard.press('ArrowUp')
|
|
608
|
+
await page.keyboard.press('ArrowUp')
|
|
609
|
+
await expect(defaultInput).toHaveValue('1')
|
|
610
|
+
await expect(max5Input).toHaveValue('2')
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
test('form integration works correctly', async ({ page }) => {
|
|
614
|
+
// Create a form wrapper around the interactive input test component
|
|
615
|
+
await page.evaluate(() => {
|
|
616
|
+
const form = document.createElement('form')
|
|
617
|
+
form.id = 'test-form'
|
|
618
|
+
const spinbutton = document.querySelector('#interactive-input-test')
|
|
619
|
+
if (spinbutton?.parentNode) {
|
|
620
|
+
spinbutton.parentNode.insertBefore(form, spinbutton)
|
|
621
|
+
form.appendChild(spinbutton)
|
|
622
|
+
}
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
const incrementButton = page.locator(
|
|
626
|
+
'#interactive-input-test button.increment',
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
// Increment the value
|
|
630
|
+
await incrementButton.click()
|
|
631
|
+
await incrementButton.click()
|
|
632
|
+
|
|
633
|
+
// Test form data includes the input value
|
|
634
|
+
const formData = await page.evaluate(() => {
|
|
635
|
+
const form = document.querySelector('#test-form') as HTMLFormElement
|
|
636
|
+
if (!form) return null
|
|
637
|
+
const data = new FormData(form)
|
|
638
|
+
return Object.fromEntries(data.entries())
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
expect(formData).toEqual({ interactive: '2' })
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
test('input shows proper max attribute synchronization', async ({ page }) => {
|
|
645
|
+
const spinbutton = page.locator('form-spinbutton').first()
|
|
646
|
+
const input = spinbutton.locator('input.value')
|
|
647
|
+
|
|
648
|
+
// Check initial max attribute
|
|
649
|
+
await expect(input).toHaveAttribute('max', '10')
|
|
650
|
+
|
|
651
|
+
// Test different components have different max values
|
|
652
|
+
const max5Input = page.locator('#max-5-test input.value')
|
|
653
|
+
await expect(max5Input).toHaveAttribute('max', '5')
|
|
654
|
+
|
|
655
|
+
const initialTestInput = page.locator('#initial-value-test input.value')
|
|
656
|
+
await expect(initialTestInput).toHaveAttribute('max', '15')
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
test('handles enabled input field interactions', async ({ page }) => {
|
|
660
|
+
// Test with interactive-input-test which has an enabled input
|
|
661
|
+
const spinbutton = page.locator('#interactive-input-test')
|
|
662
|
+
const input = spinbutton.locator('input.value')
|
|
663
|
+
const incrementButton = spinbutton.locator('button.increment')
|
|
664
|
+
|
|
665
|
+
// Initially hidden because value is 0
|
|
666
|
+
await expect(input).toBeHidden()
|
|
667
|
+
|
|
668
|
+
// Click increment to make input visible
|
|
669
|
+
await incrementButton.click()
|
|
670
|
+
await expect(input).toBeVisible()
|
|
671
|
+
await expect(input).toHaveValue('1')
|
|
672
|
+
|
|
673
|
+
// Now we can interact with the input directly
|
|
674
|
+
await input.focus()
|
|
675
|
+
await input.fill('4')
|
|
676
|
+
|
|
677
|
+
// After blur, the value should be updated
|
|
678
|
+
await input.blur()
|
|
679
|
+
await expect(input).toHaveValue('4')
|
|
680
|
+
|
|
681
|
+
// Component property should reflect the change
|
|
682
|
+
const valueProperty = await page.evaluate(() => {
|
|
683
|
+
const element = document.querySelector('#interactive-input-test') as any
|
|
684
|
+
return element.value
|
|
685
|
+
})
|
|
686
|
+
expect(valueProperty).toBe(4)
|
|
687
|
+
})
|
|
688
|
+
})
|