diff --git a/frontend-reactive/app/components/passkey.hbs b/frontend-reactive/app/components/auth.hbs similarity index 100% rename from frontend-reactive/app/components/passkey.hbs rename to frontend-reactive/app/components/auth.hbs diff --git a/frontend-reactive/app/components/auth.ts b/frontend-reactive/app/components/auth.ts new file mode 100644 index 0000000..90ac277 --- /dev/null +++ b/frontend-reactive/app/components/auth.ts @@ -0,0 +1,43 @@ +import { action } from '@ember/object'; +import { service } from '@ember/service'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import '@simplewebauthn/browser'; +import { + startAuthentication, + startRegistration, +} from '@simplewebauthn/browser'; +import type AuthService from 'frontend-reactive/services/auth'; + +export interface PasskeySignature { + // The arguments accepted by the component + Args: {}; + // Any blocks yielded by the component + Blocks: { + default: []; + }; + // The element to which `...attributes` is applied in the component template + Element: null; +} + +export default class Auth extends Component { + @tracked username: string = ''; + @tracked error: string | undefined; + @service declare auth: AuthService; + + @action async startLogin() { + try { + this.auth.startLogin(this.username); + } catch (error: any) { + this.error = 'Error: ' + error.message; + } + } + + @action async startRegistration() { + try { + this.auth.startRegistration(this.username); + } catch (error: any) { + this.error = 'Error: ' + error.message; + } + } +} diff --git a/frontend-reactive/app/components/auth/registration-form.hbs b/frontend-reactive/app/components/auth/registration-form.hbs new file mode 100644 index 0000000..5fa0ccf --- /dev/null +++ b/frontend-reactive/app/components/auth/registration-form.hbs @@ -0,0 +1,18 @@ +
+

{{this.username}}

+ + + + +

{{this.extracted}}

+
\ No newline at end of file diff --git a/frontend-reactive/app/components/auth/registration-form.ts b/frontend-reactive/app/components/auth/registration-form.ts new file mode 100644 index 0000000..37698f8 --- /dev/null +++ b/frontend-reactive/app/components/auth/registration-form.ts @@ -0,0 +1,21 @@ +import Component from '@glimmer/component' +import { tracked } from '@glimmer/tracking' + +export interface AuthRegistrationFormSignature { + // The arguments accepted by the component + Args: { + username: string + } + // Any blocks yielded by the component + Blocks: { + default: [] + } + // The element to which `...attributes` is applied in the component template + Element: null +} + +export default class AuthRegistrationForm extends Component { + @tracked displayname: string = this.args.username + @tracked description: string = '' + @tracked gender: Array<{ value: string }> = [] +} diff --git a/frontend-reactive/app/components/passkey.ts b/frontend-reactive/app/components/passkey.ts deleted file mode 100644 index d275a4c..0000000 --- a/frontend-reactive/app/components/passkey.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { action } from '@ember/object'; -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import '@simplewebauthn/browser'; -import { - startAuthentication, - startRegistration, -} from '@simplewebauthn/browser'; - -export interface PasskeySignature { - // The arguments accepted by the component - Args: {}; - // Any blocks yielded by the component - Blocks: { - default: []; - }; - // The element to which `...attributes` is applied in the component template - Element: null; -} - -export default class Passkey extends Component { - @tracked username: string = ''; - @tracked error: string | undefined; - - @action async startLogin() { - try { - // Get login options from your server. Here, we also receive the challenge. - const response = await fetch('/webauthn/passkey/loginBegin', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username: this.username }), - }); - // Check if the login options are ok. - if (!response.ok) { - const msg = await response.json(); - throw new Error('Failed to get login options from server: ' + msg); - } - // Convert the login options to JSON. - const options = await response.json(); - - // This triggers the browser to display the passkey / WebAuthn modal (e.g. Face ID, Touch ID, Windows Hello). - // A new assertionResponse is created. This also means that the challenge has been signed. - const assertionResponse = await startAuthentication(options.publicKey); - - // Send assertionResponse back to server for verification. - const verificationResponse = await fetch( - '/webauthn/passkey/loginFinish', - { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(assertionResponse), - }, - ); - - const msg = await verificationResponse.json(); - if (verificationResponse.ok) { - this.error = undefined; - } else { - this.error = msg; - } - } catch (error: any) { - this.error = 'Error: ' + error.message; - } - } - - @action async startRegistration() { - try { - // Get registration options from your server. Here, we also receive the challenge. - const response = await fetch('/webauthn/passkey/registerBegin', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username: this.username }), - }); - - // Check if the registration options are ok. - if (!response.ok) { - const msg = await response.json(); - throw new Error( - 'User already exists or failed to get registration options from server: ' + - msg, - ); - } - - // Convert the registration options to JSON. - const options = await response.json(); - console.log('registration start', options); - - // This triggers the browser to display the passkey / WebAuthn modal (e.g. Face ID, Touch ID, Windows Hello). - // A new attestation is created. This also means a new public-private-key pair is created. - const attestationResponse = await startRegistration(options.publicKey); - - console.log('Attempting to complete registration', attestationResponse); - // Send attestationResponse back to server for verification and storage. - const verificationResponse = await fetch( - '/webauthn/passkey/registerFinish', - { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(attestationResponse), - }, - ); - - const msg = await verificationResponse.json(); - if (verificationResponse.ok) { - this.error = undefined; - } else { - this.error = msg; - } - } catch (error: any) { - this.error = 'Error: ' + error.message; - } - } -} diff --git a/frontend-reactive/app/components/util/string-array.hbs b/frontend-reactive/app/components/util/string-array.hbs new file mode 100644 index 0000000..4bfd519 --- /dev/null +++ b/frontend-reactive/app/components/util/string-array.hbs @@ -0,0 +1,30 @@ +
+
    + {{#each this.args.list as |element index|}} +
  • +
    + +
    + X +
    +
    +
  • + {{/each}} + +
+
+ Add element +
+
\ No newline at end of file diff --git a/frontend-reactive/app/components/util/string-array.ts b/frontend-reactive/app/components/util/string-array.ts new file mode 100644 index 0000000..a0c2684 --- /dev/null +++ b/frontend-reactive/app/components/util/string-array.ts @@ -0,0 +1,57 @@ +import MutableArray from '@ember/array/mutable' +import { action } from '@ember/object' +import Component from '@glimmer/component' +import { tracked } from '@glimmer/tracking' + +export interface UtilStringArraySignature { + // The arguments accepted by the component + Args: { + list: MutableArray<{ value: string }> + prefix: string + } + // Any blocks yielded by the component + Blocks: { + default: [] + } + // The element to which `...attributes` is applied in the component template + Element: null +} + +export default class UtilStringArray extends Component { + @action addElement() { + MutableArray.apply(this.args.list) + this.args.list.pushObject({ value: '' }) + } + + @action removeElement(event: MouseEvent) { + MutableArray.apply(this.args.list) + const target = event.target as HTMLDivElement + const splits = target.id.split('-', 2) + if (splits.length != 2) return + const indexStr = splits[1] + //console.log('Content: ', indexStr) + if (!indexStr) return + //let index = this.args.list.find((elem) => elem == content) + //let index = this.listCopy.findIndex((d) => d == content) + this.args.list.removeAt(Number(indexStr)) + } + + transformArrayIntoUsable(arr: Array): { [key: number]: string } { + const out: { [key: number]: string } = {} + const tmp = arr.map((elem: string, index: number) => { + out[index] = elem + return elem + }) + return out + } + + countElemsInObj(obj: any): number { + let count = 0 + + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) ++count + } + + return count + } +} diff --git a/frontend-reactive/app/router.ts b/frontend-reactive/app/router.ts index f2fff6d..2e6415d 100644 --- a/frontend-reactive/app/router.ts +++ b/frontend-reactive/app/router.ts @@ -8,4 +8,5 @@ export default class Router extends EmberRouter { Router.map(function () { this.route('about'); + this.route('registerform'); }); diff --git a/frontend-reactive/app/routes/registerform.ts b/frontend-reactive/app/routes/registerform.ts new file mode 100644 index 0000000..b494fbd --- /dev/null +++ b/frontend-reactive/app/routes/registerform.ts @@ -0,0 +1,9 @@ +import Route from '@ember/routing/route' + +export default class RegisterFormRoute extends Route { + async model() { + return { + list: [{ value: 'one' }, { value: 'two' }], + } + } +} diff --git a/frontend-reactive/app/services/auth.ts b/frontend-reactive/app/services/auth.ts new file mode 100644 index 0000000..2f5af9a --- /dev/null +++ b/frontend-reactive/app/services/auth.ts @@ -0,0 +1,122 @@ +import Service from '@ember/service'; +import { + startAuthentication, + startRegistration, +} from '@simplewebauthn/browser'; + +export default class AuthService extends Service { + async startLogin(username: string) { + // Get login options from your server. Here, we also receive the challenge. + const response = await fetch('/webauthn/passkey/loginBegin', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username: username }), + }); + // Check if the login options are ok. + if (!response.ok) { + const msg = await response.json(); + throw new Error('Failed to get login options from server: ' + msg); + } + // Convert the login options to JSON. + const options = await response.json(); + + // This triggers the browser to display the passkey / WebAuthn modal (e.g. Face ID, Touch ID, Windows Hello). + // A new assertionResponse is created. This also means that the challenge has been signed. + const assertionResponse = await startAuthentication(options.publicKey); + + // Send assertionResponse back to server for verification. + const verificationResponse = await fetch('/webauthn/passkey/loginFinish', { + method: 'POST', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(assertionResponse), + }); + + const msg = await verificationResponse.json(); + if (verificationResponse.ok) { + return; + } else { + throw new Error( + 'Bad response code: ' + + verificationResponse.status + + '. Content: ' + + msg, + ); + } + } + + async startRegistration(username: string) { + // Get registration options from your server. Here, we also receive the challenge. + const response = await fetch('/webauthn/passkey/registerBegin', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username: username }), + }); + + // Check if the registration options are ok. + if (!response.ok) { + const msg = await response.json(); + throw new Error( + 'User already exists or failed to get registration options from server: ' + + msg, + ); + } + + // Convert the registration options to JSON. + const options = await response.json(); + console.log('registration start', options); + + // This triggers the browser to display the passkey / WebAuthn modal (e.g. Face ID, Touch ID, Windows Hello). + // A new attestation is created. This also means a new public-private-key pair is created. + const attestationResponse = await startRegistration(options.publicKey); + + console.log('Attempting to complete registration', attestationResponse); + // Send attestationResponse back to server for verification and storage. + const verificationResponse = await fetch( + '/webauthn/passkey/registerFinish', + { + method: 'POST', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(attestationResponse), + }, + ); + + const msg = await verificationResponse.json(); + if (verificationResponse.ok) { + return; + } else { + throw new Error( + 'Bad response code: ' + + verificationResponse.status + + '. Content: ' + + msg, + ); + } + } + + // Check if the client is currently logged in + // Happens via caling an endpoint that's only available if logged in but doesn't return anything other than "ok" + async getLoginState(): Promise { + try { + const response = await fetch('/api/test-auth'); + return response.status >= 200 && response.status < 300; + } catch (error: any) { + return false; + } + } +} + +// Don't remove this declaration: this is what enables TypeScript to resolve +// this service using `Owner.lookup('service:auth')`, as well +// as to check when you pass the service name as an argument to the decorator, +// like `@service('auth') declare altName: AuthService;`. +declare module '@ember/service' { + interface Registry { + auth: AuthService; + } +} diff --git a/frontend-reactive/app/styles/app.css b/frontend-reactive/app/styles/app.css index 7631e2d..a9dbedd 100644 --- a/frontend-reactive/app/styles/app.css +++ b/frontend-reactive/app/styles/app.css @@ -8,3 +8,4 @@ @import url("svgs.css"); @import url("timeline.css"); @import url("auth.css"); +@import url("stringArray.css"); diff --git a/frontend-reactive/app/styles/stringArray.css b/frontend-reactive/app/styles/stringArray.css new file mode 100644 index 0000000..c863b86 --- /dev/null +++ b/frontend-reactive/app/styles/stringArray.css @@ -0,0 +1,4 @@ +.string-array-element-wrapper { + display: flex; + flex-direction: row; +} diff --git a/frontend-reactive/app/templates/application.hbs b/frontend-reactive/app/templates/application.hbs index afe955e..274f120 100644 --- a/frontend-reactive/app/templates/application.hbs +++ b/frontend-reactive/app/templates/application.hbs @@ -1,4 +1,4 @@ {{page-title "FrontendReactive"}} -{{outlet}} - \ No newline at end of file +{{outlet}}{{!----}} +{{!----}} \ No newline at end of file diff --git a/frontend-reactive/app/templates/registerform.hbs b/frontend-reactive/app/templates/registerform.hbs new file mode 100644 index 0000000..bd2907d --- /dev/null +++ b/frontend-reactive/app/templates/registerform.hbs @@ -0,0 +1,2 @@ +{{page-title "RegisterForm"}} + \ No newline at end of file diff --git a/frontend-reactive/bun.lockb b/frontend-reactive/bun.lockb index df6f626..30a60dd 100755 Binary files a/frontend-reactive/bun.lockb and b/frontend-reactive/bun.lockb differ diff --git a/frontend-reactive/package-lock.json b/frontend-reactive/package-lock.json index 23c08fa..366a6ec 100644 --- a/frontend-reactive/package-lock.json +++ b/frontend-reactive/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "license": "EUPL-1.2", "dependencies": { + "@simplewebauthn/browser": "^11.0.0", "moment": "^2.30.1" }, "devDependencies": { @@ -63,6 +64,7 @@ "ember-cli-inject-live-reload": "^2.1.0", "ember-cli-sri": "^2.1.1", "ember-cli-terser": "^4.0.2", + "ember-composable-helpers": "^5.0.0", "ember-data": "~5.3.8", "ember-fetch": "^8.1.2", "ember-infinity": "^3.0.0", @@ -6013,6 +6015,21 @@ "integrity": "sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA==", "dev": true }, + "node_modules/@simplewebauthn/browser": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-11.0.0.tgz", + "integrity": "sha512-KEGCStrl08QC2I561BzxqGiwoknblP6O1YW7jApdXLPtIqZ+vgJYAv8ssLCdm1wD8HGAHd49CJLkUF8X70x/pg==", + "license": "MIT", + "dependencies": { + "@simplewebauthn/types": "^11.0.0" + } + }, + "node_modules/@simplewebauthn/types": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@simplewebauthn/types/-/types-11.0.0.tgz", + "integrity": "sha512-b2o0wC5u2rWts31dTgBkAtSNKGX0cvL6h8QedNsKmj8O4QoLFQFR3DBVBUlpyVEhYKA+mXGUaXbcOc4JdQ3HzA==", + "license": "MIT" + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -15576,6 +15593,611 @@ "semver": "bin/semver" } }, + "node_modules/ember-composable-helpers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ember-composable-helpers/-/ember-composable-helpers-5.0.0.tgz", + "integrity": "sha512-gyUrjiSju4QwNrsCLbBpP0FL6VDFZaELNW7Kbcp60xXhjvNjncYgzm4zzYXhT+i1lLA6WEgRZ3lOGgyBORYD0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.0.0", + "broccoli-funnel": "2.0.1", + "ember-cli-babel": "^7.26.3", + "resolve": "^1.10.0" + }, + "engines": { + "node": "12.* || 14.* || >= 16" + } + }, + "node_modules/ember-composable-helpers/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/ember-composable-helpers/node_modules/@babel/runtime": { + "version": "7.12.18", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.18.tgz", + "integrity": "sha512-BogPQ7ciE6SYAUPtlm9tWbgI9+2AgqSam6QivMgXgAT+fKbgppaj4ZX15MHeLC1PVF5sNk70huBu20XxWOs8Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/ember-composable-helpers/node_modules/@types/fs-extra": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.1.0.tgz", + "integrity": "sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ember-composable-helpers/node_modules/babel-plugin-module-resolver": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz", + "integrity": "sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-babel-config": "^1.1.0", + "glob": "^7.1.2", + "pkg-up": "^2.0.0", + "reselect": "^3.0.1", + "resolve": "^1.4.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-babel-transpiler": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/broccoli-babel-transpiler/-/broccoli-babel-transpiler-7.8.1.tgz", + "integrity": "sha512-6IXBgfRt7HZ61g67ssBc6lBb3Smw3DPZ9dEYirgtvXWpRZ2A9M22nxy6opEwJDgDJzlu/bB7ToppW33OFkA1gA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.12.0", + "@babel/polyfill": "^7.11.5", + "broccoli-funnel": "^2.0.2", + "broccoli-merge-trees": "^3.0.2", + "broccoli-persistent-filter": "^2.2.1", + "clone": "^2.1.2", + "hash-for-dep": "^1.4.7", + "heimdalljs": "^0.2.1", + "heimdalljs-logger": "^0.1.9", + "json-stable-stringify": "^1.0.1", + "rsvp": "^4.8.4", + "workerpool": "^3.1.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-babel-transpiler/node_modules/broccoli-funnel": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/broccoli-funnel/-/broccoli-funnel-2.0.2.tgz", + "integrity": "sha512-/vDTqtv7ipjEZQOVqO4vGDVAOZyuYzQ/EgGoyewfOgh1M7IQAToBKZI0oAQPgMBeFPPlIbfMuAngk+ohPBuaHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-equal": "^1.0.0", + "blank-object": "^1.0.1", + "broccoli-plugin": "^1.3.0", + "debug": "^2.2.0", + "fast-ordered-set": "^1.0.0", + "fs-tree-diff": "^0.5.3", + "heimdalljs": "^0.2.0", + "minimatch": "^3.0.0", + "mkdirp": "^0.5.0", + "path-posix": "^1.0.0", + "rimraf": "^2.4.3", + "symlink-or-copy": "^1.0.0", + "walk-sync": "^0.3.1" + }, + "engines": { + "node": "^4.5 || 6.* || >= 7.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-funnel": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/broccoli-funnel/-/broccoli-funnel-2.0.1.tgz", + "integrity": "sha512-C8Lnp9TVsSSiZMGEF16C0dCiNg2oJqUKwuZ1K4kVC6qRPG/2Cj/rtB5kRCC9qEbwqhX71bDbfHROx0L3J7zXQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-equal": "^1.0.0", + "blank-object": "^1.0.1", + "broccoli-plugin": "^1.3.0", + "debug": "^2.2.0", + "fast-ordered-set": "^1.0.0", + "fs-tree-diff": "^0.5.3", + "heimdalljs": "^0.2.0", + "minimatch": "^3.0.0", + "mkdirp": "^0.5.0", + "path-posix": "^1.0.0", + "rimraf": "^2.4.3", + "symlink-or-copy": "^1.0.0", + "walk-sync": "^0.3.1" + }, + "engines": { + "node": "^4.5 || 6.* || >= 7.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-persistent-filter": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/broccoli-persistent-filter/-/broccoli-persistent-filter-2.3.1.tgz", + "integrity": "sha512-hVsmIgCDrl2NFM+3Gs4Cr2TA6UPaIZip99hN8mtkaUPgM8UeVnCbxelCvBjUBHo0oaaqP5jzqqnRVvb568Yu5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-disk-cache": "^1.2.1", + "async-promise-queue": "^1.0.3", + "broccoli-plugin": "^1.0.0", + "fs-tree-diff": "^2.0.0", + "hash-for-dep": "^1.5.0", + "heimdalljs": "^0.2.1", + "heimdalljs-logger": "^0.1.7", + "mkdirp": "^0.5.1", + "promise-map-series": "^0.2.1", + "rimraf": "^2.6.1", + "rsvp": "^4.7.0", + "symlink-or-copy": "^1.0.1", + "sync-disk-cache": "^1.3.3", + "walk-sync": "^1.0.0" + }, + "engines": { + "node": "6.* || >= 8.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-persistent-filter/node_modules/fs-tree-diff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-2.0.1.tgz", + "integrity": "sha512-x+CfAZ/lJHQqwlD64pYM5QxWjzWhSjroaVsr8PW831zOApL55qPibed0c+xebaLWVr2BnHFoHdrwOv8pzt8R5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/symlink-or-copy": "^1.2.0", + "heimdalljs-logger": "^0.1.7", + "object-assign": "^4.1.0", + "path-posix": "^1.0.0", + "symlink-or-copy": "^1.1.8" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-persistent-filter/node_modules/matcher-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-1.1.2.tgz", + "integrity": "sha512-YQ/teqaOIIfUHedRam08PB3NK7Mjct6BvzRnJmpGDm8uFXpNr1sbY4yuflI5JcEs6COpYA0FpRQhSDBf1tT95g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.2" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-persistent-filter/node_modules/walk-sync": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-1.1.4.tgz", + "integrity": "sha512-nowc9thB/Jg0KW4TgxoRjLLYRPvl3DB/98S89r4ZcJqq2B0alNcKDh6pzLkBSkPMzRSMsJghJHQi79qw0YWEkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "^3.0.3", + "ensure-posix-path": "^1.1.0", + "matcher-collection": "^1.1.1" + } + }, + "node_modules/ember-composable-helpers/node_modules/broccoli-source": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/broccoli-source/-/broccoli-source-2.1.2.tgz", + "integrity": "sha512-1lLayO4wfS0c0Sj50VfHJXNWf94FYY0WUhxj0R77thbs6uWI7USiOWFqQV5dRmhAJnoKaGN4WyLGQbgjgiYFwQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/ember-composable-helpers/node_modules/ember-cli-babel": { + "version": "7.26.11", + "resolved": "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-7.26.11.tgz", + "integrity": "sha512-JJYeYjiz/JTn34q7F5DSOjkkZqy8qwFOOxXfE6pe9yEJqWGu4qErKxlz8I22JoVEQ/aBUO+OcKTpmctvykM9YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.12.0", + "@babel/helper-compilation-targets": "^7.12.0", + "@babel/plugin-proposal-class-properties": "^7.16.5", + "@babel/plugin-proposal-decorators": "^7.13.5", + "@babel/plugin-proposal-private-methods": "^7.16.5", + "@babel/plugin-proposal-private-property-in-object": "^7.16.5", + "@babel/plugin-transform-modules-amd": "^7.13.0", + "@babel/plugin-transform-runtime": "^7.13.9", + "@babel/plugin-transform-typescript": "^7.13.0", + "@babel/polyfill": "^7.11.5", + "@babel/preset-env": "^7.16.5", + "@babel/runtime": "7.12.18", + "amd-name-resolver": "^1.3.1", + "babel-plugin-debug-macros": "^0.3.4", + "babel-plugin-ember-data-packages-polyfill": "^0.1.2", + "babel-plugin-ember-modules-api-polyfill": "^3.5.0", + "babel-plugin-module-resolver": "^3.2.0", + "broccoli-babel-transpiler": "^7.8.0", + "broccoli-debug": "^0.6.4", + "broccoli-funnel": "^2.0.2", + "broccoli-source": "^2.1.2", + "calculate-cache-key-for-tree": "^2.0.0", + "clone": "^2.1.2", + "ember-cli-babel-plugin-helpers": "^1.1.1", + "ember-cli-version-checker": "^4.1.0", + "ensure-posix-path": "^1.0.2", + "fixturify-project": "^1.10.0", + "resolve-package-path": "^3.1.0", + "rimraf": "^3.0.1", + "semver": "^5.5.0" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/ember-cli-babel/node_modules/broccoli-funnel": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/broccoli-funnel/-/broccoli-funnel-2.0.2.tgz", + "integrity": "sha512-/vDTqtv7ipjEZQOVqO4vGDVAOZyuYzQ/EgGoyewfOgh1M7IQAToBKZI0oAQPgMBeFPPlIbfMuAngk+ohPBuaHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-equal": "^1.0.0", + "blank-object": "^1.0.1", + "broccoli-plugin": "^1.3.0", + "debug": "^2.2.0", + "fast-ordered-set": "^1.0.0", + "fs-tree-diff": "^0.5.3", + "heimdalljs": "^0.2.0", + "minimatch": "^3.0.0", + "mkdirp": "^0.5.0", + "path-posix": "^1.0.0", + "rimraf": "^2.4.3", + "symlink-or-copy": "^1.0.0", + "walk-sync": "^0.3.1" + }, + "engines": { + "node": "^4.5 || 6.* || >= 7.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/ember-cli-babel/node_modules/broccoli-funnel/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ember-composable-helpers/node_modules/ember-cli-babel/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ember-composable-helpers/node_modules/ember-cli-babel/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/ember-composable-helpers/node_modules/ember-cli-version-checker": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ember-cli-version-checker/-/ember-cli-version-checker-4.1.1.tgz", + "integrity": "sha512-bzEWsTMXUGEJfxcAGWPe6kI7oHEGD3jaxUWDYPTqzqGhNkgPwXTBgoWs9zG1RaSMaOPFnloWuxRcoHi4TrYS3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-package-path": "^2.0.0", + "semver": "^6.3.0", + "silent-error": "^1.1.1" + }, + "engines": { + "node": "8.* || 10.* || >= 12.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/ember-cli-version-checker/node_modules/resolve-package-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-package-path/-/resolve-package-path-2.0.0.tgz", + "integrity": "sha512-/CLuzodHO2wyyHTzls5Qr+EFeG6RcW4u6//gjYvUfcfyuplIX1SSccU+A5A9A78Gmezkl3NBkFAMxLbzTY9TJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-root": "^0.1.1", + "resolve": "^1.13.1" + }, + "engines": { + "node": "8.* || 10.* || >= 12" + } + }, + "node_modules/ember-composable-helpers/node_modules/find-babel-config": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.2.tgz", + "integrity": "sha512-oK59njMyw2y3yxto1BCfVK7MQp/OYf4FleHu0RgosH3riFJ1aOuo/7naLDLAObfrgn3ueFhw5sAT/cp0QuJI3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^1.0.2", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/ember-composable-helpers/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ember-composable-helpers/node_modules/fixturify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fixturify/-/fixturify-1.3.0.tgz", + "integrity": "sha512-tL0svlOy56pIMMUQ4bU1xRe6NZbFSa/ABTWMxW2mH38lFGc9TrNAKWcMBQ7eIjo3wqSS8f2ICabFaatFyFmrVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^5.0.5", + "@types/minimatch": "^3.0.3", + "@types/rimraf": "^2.0.2", + "fs-extra": "^7.0.1", + "matcher-collection": "^2.0.0" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/fixturify-project": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/fixturify-project/-/fixturify-project-1.10.0.tgz", + "integrity": "sha512-L1k9uiBQuN0Yr8tA9Noy2VSQ0dfg0B8qMdvT7Wb5WQKc7f3dn3bzCbSrqlb+etLW+KDV4cBC7R1OvcMg3kcxmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fixturify": "^1.2.0", + "tmp": "^0.0.33" + } + }, + "node_modules/ember-composable-helpers/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/ember-composable-helpers/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/ember-composable-helpers/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/ember-composable-helpers/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ember-composable-helpers/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ember-composable-helpers/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ember-composable-helpers/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ember-composable-helpers/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ember-composable-helpers/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ember-composable-helpers/node_modules/pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ember-composable-helpers/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ember-composable-helpers/node_modules/reselect": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz", + "integrity": "sha512-b/6tFZCmRhtBMa4xGqiiRp9jh9Aqi2A687Lo265cN0/QohJQEBPiQ52f4QB6i0eF3yp3hmLL21LSGBcML2dlxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ember-composable-helpers/node_modules/resolve-package-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/resolve-package-path/-/resolve-package-path-3.1.0.tgz", + "integrity": "sha512-2oC2EjWbMJwvSN6Z7DbDfJMnD8MYEouaLn5eIX0j8XwPsYCVIyY9bbnX88YHVkbr8XHqvZrYbxaLPibfTYKZMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-root": "^0.1.1", + "resolve": "^1.17.0" + }, + "engines": { + "node": "10.* || >= 12" + } + }, + "node_modules/ember-composable-helpers/node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/ember-composable-helpers/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/ember-composable-helpers/node_modules/workerpool": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-3.1.2.tgz", + "integrity": "sha512-WJFA0dGqIK7qj7xPTqciWBH5DlJQzoPjsANvc3Y4hNB0SScT+Emjvt0jPPkDBUjBNngX1q9hHgt1Gfwytu6pug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "^7.3.4", + "object-assign": "4.1.1", + "rsvp": "^4.8.4" + } + }, "node_modules/ember-cookies": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/ember-cookies/-/ember-cookies-1.1.2.tgz", diff --git a/frontend-reactive/package.json b/frontend-reactive/package.json index 669d7cc..2fde21a 100644 --- a/frontend-reactive/package.json +++ b/frontend-reactive/package.json @@ -77,6 +77,7 @@ "ember-cli-inject-live-reload": "^2.1.0", "ember-cli-sri": "^2.1.1", "ember-cli-terser": "^4.0.2", + "ember-composable-helpers": "^5.0.0", "ember-data": "~5.3.8", "ember-fetch": "^8.1.2", "ember-infinity": "^3.0.0", diff --git a/frontend-reactive/tests/integration/components/auth/registration-form-test.ts b/frontend-reactive/tests/integration/components/auth/registration-form-test.ts new file mode 100644 index 0000000..0da1110 --- /dev/null +++ b/frontend-reactive/tests/integration/components/auth/registration-form-test.ts @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'frontend-reactive/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | auth/registration-form', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.dom().hasText(''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.dom().hasText('template block text'); + }); +}); diff --git a/frontend-reactive/tests/integration/components/util/string-array-test.ts b/frontend-reactive/tests/integration/components/util/string-array-test.ts new file mode 100644 index 0000000..1969538 --- /dev/null +++ b/frontend-reactive/tests/integration/components/util/string-array-test.ts @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'frontend-reactive/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | util/string-array', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.dom().hasText(''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.dom().hasText('template block text'); + }); +}); diff --git a/frontend-reactive/tests/integration/helpers/binding-array-element-test.ts b/frontend-reactive/tests/integration/helpers/binding-array-element-test.ts new file mode 100644 index 0000000..398f8ca --- /dev/null +++ b/frontend-reactive/tests/integration/helpers/binding-array-element-test.ts @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'frontend-reactive/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Helper | bindingArrayElement', function (hooks) { + setupRenderingTest(hooks); + + // TODO: Replace this with your real tests. + test('it renders', async function (assert) { + this.set('inputValue', '1234'); + + await render(hbs`{{binding-array-element this.inputValue}}`); + + assert.dom().hasText('1234'); + }); +}); diff --git a/frontend-reactive/tests/integration/helpers/count-properties-test.ts b/frontend-reactive/tests/integration/helpers/count-properties-test.ts new file mode 100644 index 0000000..212d105 --- /dev/null +++ b/frontend-reactive/tests/integration/helpers/count-properties-test.ts @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'frontend-reactive/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Helper | countProperties', function (hooks) { + setupRenderingTest(hooks); + + // TODO: Replace this with your real tests. + test('it renders', async function (assert) { + this.set('inputValue', '1234'); + + await render(hbs`{{count-properties this.inputValue}}`); + + assert.dom().hasText('1234'); + }); +}); diff --git a/frontend-reactive/tests/unit/routes/register-form-test.ts b/frontend-reactive/tests/unit/routes/register-form-test.ts new file mode 100644 index 0000000..17c6a87 --- /dev/null +++ b/frontend-reactive/tests/unit/routes/register-form-test.ts @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'frontend-reactive/tests/helpers'; + +module('Unit | Route | registerForm', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + const route = this.owner.lookup('route:register-form'); + assert.ok(route); + }); +}); diff --git a/frontend-reactive/tests/unit/services/auth-test.ts b/frontend-reactive/tests/unit/services/auth-test.ts new file mode 100644 index 0000000..2251496 --- /dev/null +++ b/frontend-reactive/tests/unit/services/auth-test.ts @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'frontend-reactive/tests/helpers'; + +module('Unit | Service | auth', function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + const service = this.owner.lookup('service:auth'); + assert.ok(service); + }); +});