brut 0.0.28 → 0.0.29

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.
Files changed (369) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.projections.json +10 -0
  4. data/.rspec +3 -0
  5. data/Dockerfile.dx +32 -14
  6. data/Gemfile.lock +1 -1
  7. data/assets/Logo-Square.pxd +0 -0
  8. data/assets/LogoPylon.pxd +0 -0
  9. data/assets/LogoStop.pxd +0 -0
  10. data/assets/LogoTall.pxd +0 -0
  11. data/bin/docs +24 -2
  12. data/bin/rspec +27 -0
  13. data/bin/setup +3 -3
  14. data/brutrb.com/.vitepress/theme/custom.css +7 -0
  15. data/brutrb.com/.vitepress/theme/style.css +26 -15
  16. data/brutrb.com/deployment.md +123 -45
  17. data/brutrb.com/images/LogoPylon.png +0 -0
  18. data/brutrb.com/images/LogoSquare.png +0 -0
  19. data/brutrb.com/images/LogoStop.png +0 -0
  20. data/brutrb.com/images/LogoTall.png +0 -0
  21. data/brutrb.com/images/OverviewMetro.graffle +0 -0
  22. data/brutrb.com/images/OverviewMetro.png +0 -0
  23. data/brutrb.com/index.md +4 -3
  24. data/brutrb.com/overview.md +6 -6
  25. data/docker-compose.dx.yml +5 -2
  26. data/docs/404.html +3 -3
  27. data/docs/ai.html +4 -4
  28. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  29. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  30. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  31. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  32. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  33. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  34. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  35. data/docs/api/Brut/BackEnd.html +1 -1
  36. data/docs/api/Brut/CLI/App.html +1 -1
  37. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  38. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  39. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  40. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  41. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  42. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  43. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  45. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  46. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  47. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  48. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  50. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  54. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  58. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  59. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  60. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  61. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  62. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  63. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  64. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  65. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  66. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  67. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  68. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  69. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  70. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  71. data/docs/api/Brut/CLI/Apps.html +1 -1
  72. data/docs/api/Brut/CLI/Command.html +1 -1
  73. data/docs/api/Brut/CLI/Error.html +1 -1
  74. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  75. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  76. data/docs/api/Brut/CLI/Executor.html +1 -1
  77. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  78. data/docs/api/Brut/CLI/Options.html +1 -1
  79. data/docs/api/Brut/CLI/Output.html +1 -1
  80. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  81. data/docs/api/Brut/CLI.html +1 -1
  82. data/docs/api/Brut/FactoryBot.html +1 -1
  83. data/docs/api/Brut/Framework/App.html +1 -1
  84. data/docs/api/Brut/Framework/Config.html +1 -1
  85. data/docs/api/Brut/Framework/Container.html +1 -1
  86. data/docs/api/Brut/Framework/Error.html +1 -1
  87. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  88. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  89. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  90. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  91. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  92. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  93. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  94. data/docs/api/Brut/Framework/Errors.html +1 -1
  95. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  96. data/docs/api/Brut/Framework/MCP.html +1 -1
  97. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  98. data/docs/api/Brut/Framework.html +1 -1
  99. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  100. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  101. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  102. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  103. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  104. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  105. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  107. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
  108. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  109. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
  110. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
  111. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  112. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  113. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  115. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  116. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  117. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  118. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +201 -0
  122. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +535 -0
  123. data/docs/api/Brut/FrontEnd/Forms/Input.html +983 -35
  124. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +29 -19
  126. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +9 -3
  127. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  128. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +9 -3
  129. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  130. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  132. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  133. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  135. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  136. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  137. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  141. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  142. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  143. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  144. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  147. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  148. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  149. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  150. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  151. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  152. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  153. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  154. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  155. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  156. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  157. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  158. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  159. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  160. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  161. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  162. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  163. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  165. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  166. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  167. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  168. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  169. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  170. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  171. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  172. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  173. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  174. data/docs/api/Brut/FrontEnd.html +1 -1
  175. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  176. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  177. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  178. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  179. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  180. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  181. data/docs/api/Brut/I18n.html +1 -1
  182. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  183. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  184. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  185. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  186. data/docs/api/Brut/Instrumentation.html +1 -1
  187. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  188. data/docs/api/Brut/SinatraHelpers.html +1 -1
  189. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  190. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  191. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  192. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  193. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  194. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  195. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  196. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  197. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  198. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  199. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  200. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  201. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  202. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  203. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  204. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  205. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  206. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  207. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  208. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  209. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  210. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  211. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  212. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  213. data/docs/api/Brut/SpecSupport.html +1 -1
  214. data/docs/api/Brut.html +1 -1
  215. data/docs/api/Clock.html +1 -1
  216. data/docs/api/RichString.html +1 -1
  217. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  218. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  219. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  220. data/docs/api/Sequel/Extensions.html +1 -1
  221. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  222. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  223. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  224. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  225. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  226. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  227. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  228. data/docs/api/Sequel/Plugins.html +1 -1
  229. data/docs/api/Sequel.html +1 -1
  230. data/docs/api/_index.html +15 -1
  231. data/docs/api/class_list.html +1 -1
  232. data/docs/api/css/full_list.css +2 -1
  233. data/docs/api/css/style.css +14 -13
  234. data/docs/api/file.README.html +1 -1
  235. data/docs/api/index.html +1 -1
  236. data/docs/api/method_list.html +419 -275
  237. data/docs/api/top-level-namespace.html +1 -1
  238. data/docs/assets/LogoStop.X8x-4riz.png +0 -0
  239. data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
  240. data/docs/assets/{app.BX81XO4N.js → app.BhrfSt68.js} +1 -1
  241. data/docs/assets/chunks/@localSearchIndexroot.CeRAdP1K.js +1 -0
  242. data/docs/assets/chunks/{VPLocalSearchBox.gABXcTWp.js → VPLocalSearchBox.Dpot_2H4.js} +1 -1
  243. data/docs/assets/chunks/{theme.DwUXXAL3.js → theme.N2SNVLgU.js} +2 -2
  244. data/docs/assets/{configuration.md.BGHl8oRC.js → configuration.md.LG-zIBww.js} +1 -1
  245. data/docs/assets/deployment.md.BLseERGV.js +48 -0
  246. data/docs/assets/deployment.md.BLseERGV.lean.js +1 -0
  247. data/docs/assets/{getting-started.md.Ciz82L0m.js → getting-started.md.Dj0qtZI2.js} +2 -2
  248. data/docs/assets/index.md.CuBB-BdM.js +1 -0
  249. data/docs/assets/index.md.CuBB-BdM.lean.js +1 -0
  250. data/docs/assets/{overview.md.C5wlBcR5.js → overview.md.DVKRM8zl.js} +4 -4
  251. data/docs/assets/overview.md.DVKRM8zl.lean.js +1 -0
  252. data/docs/assets/{style.D73IYGCX.css → style.B2o1L9eN.css} +1 -1
  253. data/docs/assets.html +4 -4
  254. data/docs/brut-js/api/AjaxSubmit.html +1 -1
  255. data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
  256. data/docs/brut-js/api/Autosubmit.html +1 -1
  257. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  258. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  259. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  260. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  261. data/docs/brut-js/api/BufferedLogger.html +1 -1
  262. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  263. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  264. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  265. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  266. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  267. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  268. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  269. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  270. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  271. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  272. data/docs/brut-js/api/Form.html +1 -1
  273. data/docs/brut-js/api/Form.js.html +1 -1
  274. data/docs/brut-js/api/I18nTranslation.html +1 -1
  275. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  276. data/docs/brut-js/api/LocaleDetection.html +1 -1
  277. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  278. data/docs/brut-js/api/Logger.html +1 -1
  279. data/docs/brut-js/api/Logger.js.html +1 -1
  280. data/docs/brut-js/api/Message.html +1 -1
  281. data/docs/brut-js/api/Message.js.html +1 -1
  282. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  283. data/docs/brut-js/api/RichString.html +1 -1
  284. data/docs/brut-js/api/RichString.js.html +1 -1
  285. data/docs/brut-js/api/Tabs.html +1 -1
  286. data/docs/brut-js/api/Tabs.js.html +1 -1
  287. data/docs/brut-js/api/Tracing.html +1 -1
  288. data/docs/brut-js/api/Tracing.js.html +1 -1
  289. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  290. data/docs/brut-js/api/external-Performance.html +1 -1
  291. data/docs/brut-js/api/external-Promise.html +1 -1
  292. data/docs/brut-js/api/external-ValidityState.html +1 -1
  293. data/docs/brut-js/api/external-Window.html +1 -1
  294. data/docs/brut-js/api/external-fetch.html +1 -1
  295. data/docs/brut-js/api/global.html +1 -1
  296. data/docs/brut-js/api/index.html +1 -1
  297. data/docs/brut-js/api/index.js.html +1 -1
  298. data/docs/brut-js/api/module-testing.html +1 -1
  299. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  300. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  301. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  302. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  303. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  304. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  305. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  306. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  307. data/docs/brut-js/api/testing_index.js.html +1 -1
  308. data/docs/brut-js.html +4 -4
  309. data/docs/business-logic.html +4 -4
  310. data/docs/cli.html +4 -4
  311. data/docs/components.html +4 -4
  312. data/docs/configuration.html +6 -6
  313. data/docs/css.html +4 -4
  314. data/docs/custom-element-tests.html +4 -4
  315. data/docs/database-access.html +4 -4
  316. data/docs/database-schema.html +4 -4
  317. data/docs/deployment.html +53 -6
  318. data/docs/dev-environment.html +4 -4
  319. data/docs/doc-conventions.html +4 -4
  320. data/docs/end-to-end-tests.html +4 -4
  321. data/docs/flash-and-session.html +4 -4
  322. data/docs/forms.html +4 -4
  323. data/docs/getting-started.html +7 -7
  324. data/docs/handlers.html +4 -4
  325. data/docs/hashmap.json +1 -1
  326. data/docs/hooks.html +4 -4
  327. data/docs/i18n.html +4 -4
  328. data/docs/index.html +6 -6
  329. data/docs/instrumentation.html +4 -4
  330. data/docs/javascript.html +4 -4
  331. data/docs/jobs.html +4 -4
  332. data/docs/keyword-injection.html +4 -4
  333. data/docs/layouts.html +4 -4
  334. data/docs/lsp.html +4 -4
  335. data/docs/markdown-examples.html +4 -4
  336. data/docs/middleware.html +4 -4
  337. data/docs/not-released.html +4 -4
  338. data/docs/overview.html +9 -9
  339. data/docs/pages.html +4 -4
  340. data/docs/recipes/authentication.html +4 -4
  341. data/docs/routes.html +4 -4
  342. data/docs/security.html +4 -4
  343. data/docs/seed-data.html +4 -4
  344. data/docs/space-time-continuum.html +4 -4
  345. data/docs/tutorial.html +4 -4
  346. data/docs/unit-tests.html +4 -4
  347. data/dx/bash_customizations +7 -0
  348. data/dx/build +13 -2
  349. data/dx/docker-compose.env +1 -1
  350. data/dx/exec +25 -8
  351. data/lib/brut/front_end/forms/input.rb +253 -20
  352. data/lib/brut/front_end/forms/input_definition.rb +15 -12
  353. data/lib/brut/front_end.rb +1 -0
  354. data/lib/brut/version.rb +1 -1
  355. data/specs/brut/front_end/forms/input.spec.rb +978 -0
  356. data/specs/spec_helper.rb +27 -0
  357. data/specs/support/matchers/have_constraint_violation.rb +23 -0
  358. data/specs/support/matchers.rb +5 -0
  359. data/specs/support.rb +3 -0
  360. metadata +39 -17
  361. data/docs/assets/chunks/@localSearchIndexroot.CoYzciVi.js +0 -1
  362. data/docs/assets/deployment.md.Dbka4OTr.js +0 -1
  363. data/docs/assets/deployment.md.Dbka4OTr.lean.js +0 -1
  364. data/docs/assets/index.md.B28EwVpq.js +0 -1
  365. data/docs/assets/index.md.B28EwVpq.lean.js +0 -1
  366. data/docs/assets/overview.Da81cB9R.png +0 -0
  367. data/docs/assets/overview.md.C5wlBcR5.lean.js +0 -1
  368. /data/docs/assets/{configuration.md.BGHl8oRC.lean.js → configuration.md.LG-zIBww.lean.js} +0 -0
  369. /data/docs/assets/{getting-started.md.Ciz82L0m.lean.js → getting-started.md.Dj0qtZI2.lean.js} +0 -0
@@ -0,0 +1,978 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Brut::FrontEnd::Forms::Input do
4
+ describe "#value=" do
5
+ let(:name) { "test-input" }
6
+ [
7
+ "text",
8
+ "search",
9
+ "password",
10
+ "tel",
11
+ ].each do |type|
12
+ context "type=#{type}" do
13
+ context "value is present" do
14
+ let(:input) {
15
+ described_class.new(
16
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
17
+ type:,
18
+ name:,
19
+ minlength: 5,
20
+ maxlength: 10,
21
+ pattern: "^[0-9]+$",
22
+ required: true,
23
+ ),
24
+ index: 0,
25
+ value: "123456",
26
+ )
27
+ }
28
+ context "value falls within all constraints" do
29
+ it "does not set any violations" do
30
+ input.value = "123456"
31
+ expect(input.valid?).to eq(true)
32
+ end
33
+ end
34
+ context "value's length is the minlength" do
35
+ it "does not set any violations" do
36
+ input.value = "12345"
37
+ expect(input.valid?).to eq(true)
38
+ end
39
+ end
40
+ context "value's length is the maxlength" do
41
+ it "does not set any violations" do
42
+ input.value = "1234567890"
43
+ expect(input.valid?).to eq(true)
44
+ end
45
+ end
46
+ context "value's length is below minlength" do
47
+ it "sets tooShort" do
48
+ input.value = "1234"
49
+ expect(input.validity_state).to have_constraint_violation(:tooShort)
50
+ end
51
+ end
52
+ context "value's length is above maxlength" do
53
+ it "sets tooLong" do
54
+ input.value = "12345678901"
55
+ expect(input.validity_state).to have_constraint_violation(:tooLong)
56
+ end
57
+ end
58
+ context "value's length is fine, but doesn't match pattern" do
59
+ it "sets patternMismatch" do
60
+ input.value = "abcdefgh"
61
+ expect(input.validity_state).to have_constraint_violation(:patternMismatch)
62
+ end
63
+ end
64
+ end
65
+ context "value is blank" do
66
+ context ":required is true" do
67
+ it "sets valueMissing, but not any others" do
68
+ input = described_class.new(
69
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
70
+ type:,
71
+ name:,
72
+ required: true,
73
+ minlength: 1,
74
+ maxlength: 2,
75
+ pattern: "foo",
76
+ ),
77
+ index: 0,
78
+ value: "",
79
+ )
80
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
81
+ expect(input.validity_state).not_to have_constraint_violation(:tooShort)
82
+ expect(input.validity_state).not_to have_constraint_violation(:patternMismatch)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ context "type=number" do
89
+ context "value is present" do
90
+ context "value is a number" do
91
+ let(:input) {
92
+ described_class.new(
93
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
94
+ type: "number",
95
+ name:,
96
+ min: -10,
97
+ max: 20,
98
+ step: 0.1,
99
+ required: true,
100
+ ),
101
+ index: 0,
102
+ value: "0",
103
+ )
104
+ }
105
+ context "value falls within all constraints" do
106
+ it "does not set any violations" do
107
+ input.value = 0
108
+ expect(input.valid?).to eq(true)
109
+ expect(input.value).to eq("0")
110
+ expect(input.typed_value).to eq(BigDecimal("0"))
111
+ end
112
+ end
113
+ context "value is at minimum" do
114
+ it "does not set any violations" do
115
+ input.value = -10
116
+ expect(input.valid?).to eq(true)
117
+ expect(input.value).to eq("-10")
118
+ expect(input.typed_value).to eq(BigDecimal("-10"))
119
+ end
120
+ end
121
+ context "value is at maximum" do
122
+ it "does not set any violations" do
123
+ input.value = 20
124
+ expect(input.valid?).to eq(true)
125
+ expect(input.value).to eq("20")
126
+ expect(input.typed_value).to eq(BigDecimal("20"))
127
+ end
128
+ end
129
+ context "value is below minimum" do
130
+ it "sets rangeUnderflow" do
131
+ input.value = -11
132
+ expect(input.validity_state).to have_constraint_violation(:rangeUnderflow)
133
+ expect(input.value).to eq("-11")
134
+ expect(input.typed_value).to eq(BigDecimal("-11"))
135
+ end
136
+ end
137
+ context "value is above maximum" do
138
+ it "sets rangeOverflow" do
139
+ input.value = 21
140
+ expect(input.validity_state).to have_constraint_violation(:rangeOverflow)
141
+ expect(input.value).to eq("21")
142
+ expect(input.typed_value).to eq(BigDecimal("21"))
143
+ end
144
+ end
145
+ context "value is not a multiple of step" do
146
+ it "sets stepMismatch" do
147
+ input.value = 0.06
148
+ expect(input.validity_state).to have_constraint_violation(:stepMismatch)
149
+ expect(input.value).to eq("0.06")
150
+ expect(input.typed_value).to eq(BigDecimal("0.06"))
151
+ end
152
+ end
153
+ end
154
+ context "value is not a number" do
155
+ it "treats the number as blank" do
156
+ input = described_class.new(
157
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
158
+ type: "number",
159
+ name:,
160
+ min: -10,
161
+ max: 20,
162
+ step: 0.1,
163
+ required: true,
164
+ ),
165
+ index: 0,
166
+ value: "not a number",
167
+ )
168
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
169
+ expect(input.value).to eq(nil)
170
+ expect(input.typed_value).to eq(nil)
171
+ end
172
+ end
173
+ end
174
+ context "value is blank and required" do
175
+ it "sets valueMissing" do
176
+ input = described_class.new(
177
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
178
+ type: "number",
179
+ name:,
180
+ min: -10,
181
+ max: 20,
182
+ step: 0.1,
183
+ required: true,
184
+ ),
185
+ index: 0,
186
+ value: "",
187
+ )
188
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
189
+ expect(input.value).to eq(nil)
190
+ expect(input.typed_value).to eq(nil)
191
+ end
192
+ end
193
+ end
194
+ context "type=checkbox" do
195
+ context "value is 'true'" do
196
+ it "has no constraint violations and its typed_value is boolean true" do
197
+ input = described_class.new(
198
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
199
+ type: "checkbox",
200
+ name:,
201
+ ),
202
+ index: 0,
203
+ value: "true"
204
+ )
205
+ expect(input.valid?).to eq(true)
206
+ expect(input.value).to eq("true")
207
+ expect(input.typed_value).to eq(true)
208
+ end
209
+ end
210
+ context "value is not 'true'" do
211
+ context "it's not required" do
212
+ it "has no constraint violations and its typed_value is boolean false" do
213
+ input = described_class.new(
214
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
215
+ type: "checkbox",
216
+ name:,
217
+ ),
218
+ index: 0,
219
+ value: "not true"
220
+ )
221
+ expect(input.valid?).to eq(true)
222
+ expect(input.value).to eq("not true")
223
+ expect(input.typed_value).to eq(false)
224
+ end
225
+ end
226
+ context "it is required" do
227
+ it "has valueMissing set and its typed_value is boolean false" do
228
+ input = described_class.new(
229
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
230
+ type: "checkbox",
231
+ name:,
232
+ required: true,
233
+ ),
234
+ index: 0,
235
+ value: "not true"
236
+ )
237
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
238
+ expect(input.value).to eq("not true")
239
+ expect(input.typed_value).to eq(false)
240
+ end
241
+ end
242
+ end
243
+ end
244
+ context "type=color" do
245
+ context "value is present" do
246
+ it "does not set any violations" do
247
+ input = described_class.new(
248
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
249
+ type: "color",
250
+ name:,
251
+ required: true,
252
+ ),
253
+ index: 0,
254
+ value: "#C0FFEE",
255
+ )
256
+ expect(input.valid?).to eq(true)
257
+ expect(input.value).to eq("#C0FFEE")
258
+ expect(input.typed_value).to eq("#c0ffee")
259
+ end
260
+ it "if not a hex code, typed_value is nil" do
261
+ input = described_class.new(
262
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
263
+ type: "color",
264
+ name:,
265
+ ),
266
+ index: 0,
267
+ value: "#abc"
268
+ )
269
+ expect(input.valid?).to eq(true)
270
+ expect(input.value).to eq(nil)
271
+ expect(input.typed_value).to eq(nil)
272
+ end
273
+ end
274
+ end
275
+ context "type=date" do
276
+ context "value is present" do
277
+ context "value is a date" do
278
+ let(:input) {
279
+ described_class.new(
280
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
281
+ type: "date",
282
+ name:,
283
+ min: "2023-01-01",
284
+ max: "2023-12-31",
285
+ step: 1,
286
+ required: true,
287
+ ),
288
+ index: 0,
289
+ value: "2023-06-15",
290
+ )
291
+ }
292
+ context "value falls within all constraints" do
293
+ it "does not set any violations" do
294
+ input.value = "2023-06-15"
295
+ expect(input.valid?).to eq(true)
296
+ expect(input.value).to eq("2023-06-15")
297
+ expect(input.typed_value).to eq(Date.new(2023, 6, 15))
298
+ end
299
+ end
300
+ context "value is at minimum" do
301
+ it "does not set any violations" do
302
+ input.value = "2023-01-01"
303
+ expect(input.valid?).to eq(true)
304
+ expect(input.value).to eq("2023-01-01")
305
+ expect(input.typed_value).to eq(Date.new(2023, 1, 1))
306
+ end
307
+ end
308
+ context "value is at maximum" do
309
+ it "does not set any violations" do
310
+ input.value = "2023-12-31"
311
+ expect(input.valid?).to eq(true)
312
+ expect(input.value).to eq("2023-12-31")
313
+ expect(input.typed_value).to eq(Date.new(2023, 12, 31))
314
+ end
315
+ end
316
+ context "value is below minimum" do
317
+ it "sets rangeUnderflow" do
318
+ input.value = "2022-12-31"
319
+ expect(input.validity_state).to have_constraint_violation(:rangeUnderflow)
320
+ expect(input.value).to eq("2022-12-31")
321
+ expect(input.typed_value).to eq(Date.new(2022, 12, 31))
322
+ end
323
+ end
324
+ context "value is above maximum" do
325
+ it "sets rangeOverflow" do
326
+ input.value = "2024-01-01"
327
+ expect(input.validity_state).to have_constraint_violation(:rangeOverflow)
328
+ expect(input.value).to eq("2024-01-01")
329
+ expect(input.typed_value).to eq(Date.new(2024, 1, 1))
330
+ end
331
+ end
332
+ context "value is not a multiple of step" do
333
+ it "sets stepMismatch" do
334
+ input = described_class.new(
335
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
336
+ type: "date",
337
+ name:,
338
+ min: "2023-01-01",
339
+ max: "2023-12-31",
340
+ step: 10,
341
+ required: true,
342
+ ),
343
+ index: 0,
344
+ value: "2023-01-03",
345
+ )
346
+ expect(input.validity_state).to have_constraint_violation(:stepMismatch)
347
+ expect(input.value).to eq("2023-01-03")
348
+ expect(input.typed_value).to eq(Date.new(2023, 1, 3))
349
+ end
350
+ end
351
+ end
352
+ context "value is not a date" do
353
+ it "treats the date as blank" do
354
+ input = described_class.new(
355
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
356
+ type: "date",
357
+ name:,
358
+ required: true,
359
+ ),
360
+ index: 0,
361
+ value: "not a date",
362
+ )
363
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
364
+ expect(input.value).to eq(nil)
365
+ expect(input.typed_value).to eq(nil)
366
+ end
367
+ end
368
+ end
369
+ context "value is blank and required" do
370
+ it "sets valueMissing" do
371
+ input = described_class.new(
372
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
373
+ type: "date",
374
+ name:,
375
+ required: true,
376
+ ),
377
+ index: 0,
378
+ value: "",
379
+ )
380
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
381
+ expect(input.value).to eq(nil)
382
+ expect(input.typed_value).to eq(nil)
383
+ end
384
+ end
385
+ end
386
+ context "type=datetime-local" do
387
+ context "value is present" do
388
+ context "value is a date" do
389
+ let(:input) {
390
+ described_class.new(
391
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
392
+ type: "datetime-local",
393
+ name:,
394
+ min: "2021-01-03T12:30",
395
+ max: "2022-03-02T14:40",
396
+ step: 10,
397
+ required: true,
398
+ ),
399
+ index: 0,
400
+ value: "2021-05-05T13:22:20",
401
+ )
402
+ }
403
+ context "value falls within all constraints" do
404
+ it "does not set any violations" do
405
+ input.value = "2021-05-05T13:22:20"
406
+ expect(input.valid?).to eq(true)
407
+ expect(input.value).to eq("2021-05-05T13:22:20")
408
+ expect(input.typed_value).to eq(Time.new(2021, 5, 5, 13, 22, 20))
409
+ end
410
+ end
411
+ context "value is at minimum" do
412
+ it "does not set any violations" do
413
+ input.value = "2021-01-03T12:30"
414
+ expect(input.valid?).to eq(true)
415
+ expect(input.value).to eq("2021-01-03T12:30")
416
+ expect(input.typed_value).to eq(Time.new(2021, 1, 3, 12, 30))
417
+ end
418
+ end
419
+ context "value is at maximum" do
420
+ it "does not set any violations" do
421
+ input.value = "2022-03-02T14:40"
422
+ expect(input.valid?).to eq(true)
423
+ expect(input.value).to eq("2022-03-02T14:40")
424
+ expect(input.typed_value).to eq(Time.new(2022, 3, 2, 14, 40))
425
+ end
426
+ end
427
+ context "value is below minimum" do
428
+ it "sets rangeUnderflow" do
429
+ input.value = "2021-01-03T12:29:50"
430
+ expect(input.validity_state).to have_constraint_violation(:rangeUnderflow)
431
+ expect(input.value).to eq("2021-01-03T12:29:50")
432
+ expect(input.typed_value).to eq(Time.new(2021, 1, 3, 12, 29, 50))
433
+ end
434
+ end
435
+ context "value is above maximum" do
436
+ it "sets rangeOverflow" do
437
+ input.value = "2022-03-02T14:40:20"
438
+ expect(input.validity_state).to have_constraint_violation(:rangeOverflow)
439
+ expect(input.value).to eq("2022-03-02T14:40:20")
440
+ expect(input.typed_value).to eq(Time.new(2022, 3, 2, 14, 40, 20))
441
+ end
442
+ end
443
+ context "value is not a multiple of step" do
444
+ it "sets stepMismatch" do
445
+ input.value = "2021-05-05T12:34:03"
446
+ expect(input.validity_state).to have_constraint_violation(:stepMismatch)
447
+ expect(input.value).to eq("2021-05-05T12:34:03")
448
+ expect(input.typed_value).to eq(Time.new(2021, 5, 5, 12, 34, 3))
449
+ end
450
+ end
451
+ end
452
+ context "value is not a datetime-local" do
453
+ it "treats the datetime-local as blank" do
454
+ input = described_class.new(
455
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
456
+ type: "datetime-local",
457
+ name:,
458
+ required: true,
459
+ ),
460
+ index: 0,
461
+ value: "not a datetime-local",
462
+ )
463
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
464
+ expect(input.value).to eq(nil)
465
+ expect(input.typed_value).to eq(nil)
466
+ end
467
+ end
468
+ end
469
+ context "value is blank and required" do
470
+ it "sets valueMissing" do
471
+ input = described_class.new(
472
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
473
+ type: "datetime-local",
474
+ name:,
475
+ required: true,
476
+ ),
477
+ index: 0,
478
+ value: "",
479
+ )
480
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
481
+ expect(input.value).to eq(nil)
482
+ expect(input.typed_value).to eq(nil)
483
+ end
484
+ end
485
+ end
486
+ context "type=email" do
487
+ context "value is present" do
488
+ let(:input) {
489
+ described_class.new(
490
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
491
+ type: "email",
492
+ name:,
493
+ minlength: 5,
494
+ maxlength: 10,
495
+ required: true,
496
+ ),
497
+ index: 0,
498
+ value: "123456",
499
+ )
500
+ }
501
+ context "value falls within all constraints" do
502
+ it "does not set any violations" do
503
+ input.value = "a@a.com"
504
+ expect(input.valid?).to eq(true)
505
+ end
506
+ end
507
+ context "value's length is the minlength" do
508
+ it "does not set any violations" do
509
+ input.value = "a@a.a"
510
+ expect(input.valid?).to eq(true)
511
+ end
512
+ end
513
+ context "value's length is the maxlength" do
514
+ it "does not set any violations" do
515
+ input.value = "a@abcd.com"
516
+ expect(input.valid?).to eq(true)
517
+ end
518
+ end
519
+ context "value's length is below minlength" do
520
+ it "sets tooShort" do
521
+ input.value = "a@a"
522
+ expect(input.validity_state).to have_constraint_violation(:tooShort)
523
+ end
524
+ end
525
+ context "value's length is above maxlength" do
526
+ it "sets tooLong" do
527
+ input.value = "a@very-long-url.com.info"
528
+ expect(input.validity_state).to have_constraint_violation(:tooLong)
529
+ end
530
+ end
531
+ context "value's length is fine, but doesn't match pattern" do
532
+ it "sets patternMismatch" do
533
+ input.value = "abcdefg"
534
+ expect(input.validity_state).to have_constraint_violation(:patternMismatch)
535
+ end
536
+ end
537
+ it "defaults to a stricter pattern than the browser requires" do
538
+ input = described_class.new(
539
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
540
+ type: "email",
541
+ name:,
542
+ ),
543
+ index: 0,
544
+ value: "browser@allowsthis",
545
+ )
546
+ expect(input.validity_state).to have_constraint_violation(:patternMismatch)
547
+ end
548
+ it "does not override an explicitly-given pattern" do
549
+ input = described_class.new(
550
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
551
+ type: "email",
552
+ name:,
553
+ pattern: "^[^@]+@example"
554
+ ),
555
+ index: 0,
556
+ value: "foo@example",
557
+ )
558
+ expect(input.valid?).to eq(true)
559
+ end
560
+ it "can not set any pattern if given a special symbol" do
561
+ input = described_class.new(
562
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
563
+ type: "email",
564
+ name:,
565
+ pattern: nil,
566
+ ),
567
+ index: 0,
568
+ value: "browser@allowsthis",
569
+ )
570
+ expect(input.valid?).to eq(true)
571
+ end
572
+ end
573
+ context "value is blank" do
574
+ context ":required is true" do
575
+ it "sets valueMissing, but not any others" do
576
+ input = described_class.new(
577
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
578
+ type: "email",
579
+ name:,
580
+ required: true,
581
+ minlength: 1,
582
+ maxlength: 2,
583
+ pattern: "foo",
584
+ ),
585
+ index: 0,
586
+ value: "",
587
+ )
588
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
589
+ expect(input.validity_state).not_to have_constraint_violation(:tooShort)
590
+ expect(input.validity_state).not_to have_constraint_violation(:patternMismatch)
591
+ end
592
+ end
593
+ end
594
+ end
595
+ context "type=file" do
596
+ it "does not set any violations" do
597
+ input = described_class.new(
598
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
599
+ type: "file",
600
+ name:,
601
+ required: true,
602
+ ),
603
+ index: 0,
604
+ value: ""
605
+ )
606
+ expect(input.valid?).to eq(true)
607
+ end
608
+ end
609
+ context "type=hidden" do
610
+ it "does not set any violations" do
611
+ input = described_class.new(
612
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
613
+ type: "hidden",
614
+ name:,
615
+ required: true,
616
+ ),
617
+ index: 0,
618
+ value: ""
619
+ )
620
+ expect(input.valid?).to eq(true)
621
+ end
622
+ end
623
+ context "type=radio" do
624
+ context "value is present" do
625
+ it "has no constraint violations" do
626
+ input = described_class.new(
627
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
628
+ type: "radio",
629
+ name:,
630
+ ),
631
+ index: 0,
632
+ value: "some selection"
633
+ )
634
+ expect(input.valid?).to eq(true)
635
+ expect(input.value).to eq("some selection")
636
+ expect(input.typed_value).to eq("some selection")
637
+ end
638
+ end
639
+ context "value is not set" do
640
+ context "it's not required" do
641
+ it "has no constraint violations" do
642
+ input = described_class.new(
643
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
644
+ type: "radio",
645
+ name:,
646
+ required: false,
647
+ ),
648
+ index: 0,
649
+ value: ""
650
+ )
651
+ expect(input.valid?).to eq(true)
652
+ expect(input.value).to eq(nil)
653
+ expect(input.typed_value).to eq(nil)
654
+ end
655
+ end
656
+ context "it is required" do
657
+ it "has valueMissing set" do
658
+ input = described_class.new(
659
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
660
+ type: "radio",
661
+ name:,
662
+ required: true,
663
+ ),
664
+ index: 0,
665
+ value: ""
666
+ )
667
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
668
+ expect(input.value).to eq(nil)
669
+ expect(input.typed_value).to eq(nil)
670
+ end
671
+ end
672
+ end
673
+ end
674
+ context "type=range" do
675
+ context "value is present" do
676
+ context "value is a number" do
677
+ let(:input) {
678
+ described_class.new(
679
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
680
+ type: "range",
681
+ name:,
682
+ min: -10,
683
+ max: 20,
684
+ step: 0.1,
685
+ ),
686
+ index: 0,
687
+ value: "0",
688
+ )
689
+ }
690
+ context "value falls within all constraints" do
691
+ it "does not set any violations" do
692
+ input.value = 0
693
+ expect(input.valid?).to eq(true)
694
+ expect(input.value).to eq("0")
695
+ expect(input.typed_value).to eq(BigDecimal("0"))
696
+ end
697
+ end
698
+ context "value is at minimum" do
699
+ it "does not set any violations" do
700
+ input.value = -10
701
+ expect(input.valid?).to eq(true)
702
+ expect(input.value).to eq("-10")
703
+ expect(input.typed_value).to eq(BigDecimal("-10"))
704
+ end
705
+ end
706
+ context "value is at maximum" do
707
+ it "does not set any violations" do
708
+ input.value = 20
709
+ expect(input.valid?).to eq(true)
710
+ expect(input.value).to eq("20")
711
+ expect(input.typed_value).to eq(BigDecimal("20"))
712
+ end
713
+ end
714
+ context "value is below minimum" do
715
+ it "sets rangeUnderflow" do
716
+ input.value = -11
717
+ expect(input.validity_state).to have_constraint_violation(:rangeUnderflow)
718
+ expect(input.value).to eq("-11")
719
+ expect(input.typed_value).to eq(BigDecimal("-11"))
720
+ end
721
+ end
722
+ context "value is above maximum" do
723
+ it "sets rangeOverflow" do
724
+ input.value = 21
725
+ expect(input.validity_state).to have_constraint_violation(:rangeOverflow)
726
+ expect(input.value).to eq("21")
727
+ expect(input.typed_value).to eq(BigDecimal("21"))
728
+ end
729
+ end
730
+ context "value is not a multiple of step" do
731
+ it "sets stepMismatch" do
732
+ input.value = 0.06
733
+ expect(input.validity_state).to have_constraint_violation(:stepMismatch)
734
+ expect(input.value).to eq("0.06")
735
+ expect(input.typed_value).to eq(BigDecimal("0.06"))
736
+ end
737
+ end
738
+ end
739
+ context "value is not a number" do
740
+ it "treats the number as blank, which is valid, but the browser will choose the value to show by default" do
741
+ input = described_class.new(
742
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
743
+ type: "range",
744
+ name:,
745
+ min: -10,
746
+ max: 20,
747
+ step: 0.1,
748
+ ),
749
+ index: 0,
750
+ value: "not a number",
751
+ )
752
+ expect(input.valid?).to eq(true)
753
+ expect(input.value).to eq(nil)
754
+ expect(input.typed_value).to eq(nil)
755
+ end
756
+ end
757
+ end
758
+ context "value is blank" do
759
+ it "sets valueMissing" do
760
+ input = described_class.new(
761
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
762
+ type: "range",
763
+ name:,
764
+ min: -10,
765
+ max: 20,
766
+ step: 0.1,
767
+ ),
768
+ index: 0,
769
+ value: "",
770
+ )
771
+ expect(input.valid?).to eq(true)
772
+ expect(input.value).to eq(nil)
773
+ expect(input.typed_value).to eq(nil)
774
+ end
775
+ end
776
+ end
777
+ context "type=time" do
778
+ context "value is present" do
779
+ context "value is a date" do
780
+ let(:input) {
781
+ described_class.new(
782
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
783
+ type: "time",
784
+ name:,
785
+ min: "12:34:10",
786
+ max: "16:45:30",
787
+ step: 10,
788
+ required: true,
789
+ ),
790
+ index: 0,
791
+ value: "13:22:20",
792
+ )
793
+ }
794
+ context "value falls within all constraints" do
795
+ it "does not set any violations" do
796
+ input.value = "13:22:20"
797
+ expect(input.valid?).to eq(true)
798
+ expect(input.value).to eq("13:22:20")
799
+ expect(input.typed_value).to eq("13:22:20")
800
+ end
801
+ end
802
+ context "value is at minimum" do
803
+ it "does not set any violations" do
804
+ input.value = "12:34:10"
805
+ expect(input.valid?).to eq(true)
806
+ expect(input.value).to eq("12:34:10")
807
+ expect(input.typed_value).to eq("12:34:10")
808
+ end
809
+ end
810
+ context "value is at maximum" do
811
+ it "does not set any violations" do
812
+ input.value = "16:45:30"
813
+ expect(input.valid?).to eq(true)
814
+ expect(input.value).to eq("16:45:30")
815
+ expect(input.typed_value).to eq("16:45:30")
816
+ end
817
+ end
818
+ context "value is below minimum" do
819
+ it "sets rangeUnderflow" do
820
+ input.value = "12:34:00"
821
+ expect(input.validity_state).to have_constraint_violation(:rangeUnderflow)
822
+ expect(input.value).to eq("12:34:00")
823
+ expect(input.typed_value).to eq("12:34:00")
824
+ end
825
+ end
826
+ context "value is above maximum" do
827
+ it "sets rangeOverflow" do
828
+ input.value = "16:45:40"
829
+ expect(input.validity_state).to have_constraint_violation(:rangeOverflow)
830
+ expect(input.value).to eq("16:45:40")
831
+ expect(input.typed_value).to eq("16:45:40")
832
+ end
833
+ end
834
+ context "value is not a multiple of step" do
835
+ it "sets stepMismatch" do
836
+ input.value = "12:34:03"
837
+ expect(input.validity_state).to have_constraint_violation(:stepMismatch)
838
+ expect(input.value).to eq("12:34:03")
839
+ expect(input.typed_value).to eq("12:34:03")
840
+ end
841
+ end
842
+ end
843
+ context "value is not a time" do
844
+ it "treats the time as blank" do
845
+ input = described_class.new(
846
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
847
+ type: "time",
848
+ name:,
849
+ required: true,
850
+ ),
851
+ index: 0,
852
+ value: "not a time",
853
+ )
854
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
855
+ expect(input.value).to eq(nil)
856
+ expect(input.typed_value).to eq(nil)
857
+ end
858
+ end
859
+ end
860
+ context "value is blank and required" do
861
+ it "sets valueMissing" do
862
+ input = described_class.new(
863
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
864
+ type: "time",
865
+ name:,
866
+ required: true,
867
+ ),
868
+ index: 0,
869
+ value: "",
870
+ )
871
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
872
+ expect(input.value).to eq(nil)
873
+ expect(input.typed_value).to eq(nil)
874
+ end
875
+ end
876
+ end
877
+ context "type=url" do
878
+ context "value is present" do
879
+ let(:input) {
880
+ described_class.new(
881
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
882
+ type: "url",
883
+ name:,
884
+ minlength: 11,
885
+ maxlength: 30,
886
+ required: true,
887
+ ),
888
+ index: 0,
889
+ value: "http://example.com",
890
+ )
891
+ }
892
+ context "value falls within all constraints" do
893
+ it "does not set any violations" do
894
+ input.value = "https://example.net"
895
+ expect(input.valid?).to eq(true)
896
+ expect(input.typed_value.kind_of?(URI)).to eq(true)
897
+ end
898
+ end
899
+ context "value's length is the minlength" do
900
+ it "does not set any violations" do
901
+ input.value = "http://Y.co"
902
+ expect(input.valid?).to eq(true)
903
+ expect(input.typed_value.kind_of?(URI)).to eq(true)
904
+ end
905
+ end
906
+ context "value's length is the maxlength" do
907
+ it "does not set any violations" do
908
+ input.value = "http://example.com/abcdefghijk"
909
+ expect(input.valid?).to eq(true)
910
+ expect(input.typed_value.kind_of?(URI)).to eq(true)
911
+ end
912
+ end
913
+ context "value's length is below minlength" do
914
+ it "sets tooShort" do
915
+ input.value = "ftp://Y.co"
916
+ expect(input.validity_state).to have_constraint_violation(:tooShort)
917
+ end
918
+ end
919
+ context "value's length is above maxlength" do
920
+ it "sets tooLong" do
921
+ input.value = "http://example.com/abcdefghijkL"
922
+ expect(input.validity_state).to have_constraint_violation(:tooLong)
923
+ end
924
+ end
925
+ context "value's length is fine, but doesn't match pattern" do
926
+ it "sets patternMismatch" do
927
+ input = described_class.new(
928
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
929
+ type: "url",
930
+ name:,
931
+ minlength: 11,
932
+ maxlength: 30,
933
+ required: true,
934
+ pattern: "^https://.*$"
935
+ ),
936
+ index: 0,
937
+ value: "http://example.com",
938
+ )
939
+ expect(input.validity_state).to have_constraint_violation(:patternMismatch)
940
+ expect(input.typed_value.kind_of?(URI)).to eq(true)
941
+ end
942
+ end
943
+ it "by default it sets no pattern, but uses Ruby's URI as a way to check validity" do
944
+ input = described_class.new(
945
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
946
+ type: "url",
947
+ name:,
948
+ pattern: nil,
949
+ ),
950
+ index: 0,
951
+ value: "xxx/foo.info"
952
+ )
953
+ expect(input.validity_state).to have_constraint_violation(:patternMismatch)
954
+ end
955
+ end
956
+ context "value is blank" do
957
+ context ":required is true" do
958
+ it "sets valueMissing, but not any others" do
959
+ input = described_class.new(
960
+ input_definition: Brut::FrontEnd::Forms::InputDefinition.new(
961
+ type: "url",
962
+ name:,
963
+ minlength: 1,
964
+ maxlength: 2,
965
+ pattern: "foo",
966
+ ),
967
+ index: 0,
968
+ value: "",
969
+ )
970
+ expect(input.validity_state).to have_constraint_violation(:valueMissing)
971
+ expect(input.validity_state).not_to have_constraint_violation(:tooShort)
972
+ expect(input.validity_state).not_to have_constraint_violation(:patternMismatch)
973
+ end
974
+ end
975
+ end
976
+ end
977
+ end
978
+ end