More work on post registration form
This commit is contained in:
parent
952949b609
commit
8b03454d6f
27 changed files with 247 additions and 51 deletions
|
@ -45,7 +45,9 @@ Make use of the many generators for code, try `ember help generate` for more det
|
||||||
|
|
||||||
### Deploying
|
### Deploying
|
||||||
|
|
||||||
Specify what it takes to deploy your app.
|
1. Build it (see point above)
|
||||||
|
2. Build the Go server
|
||||||
|
3. Now you have a binary with everything needed embedded into it ready to use
|
||||||
|
|
||||||
## Further Reading / Useful Links
|
## Further Reading / Useful Links
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<div class="auth-wrapper">
|
||||||
|
<div class="auth-username-wrapper">
|
||||||
|
<label>
|
||||||
|
Username
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
@value={{this.username}}
|
||||||
|
placeholder="Username"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div type="button" class="auth-button-login" {{on "click" this.startLogin}}>
|
||||||
|
Login
|
||||||
|
</div>
|
||||||
|
<div type="button" class="auth-button-register" {{on "click" this.startRegistration}}>
|
||||||
|
Register
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -9,7 +9,7 @@ import {
|
||||||
} from '@simplewebauthn/browser';
|
} from '@simplewebauthn/browser';
|
||||||
import type AuthService from 'frontend-reactive/services/auth';
|
import type AuthService from 'frontend-reactive/services/auth';
|
||||||
|
|
||||||
export interface PasskeySignature {
|
export interface AuthSignature {
|
||||||
// The arguments accepted by the component
|
// The arguments accepted by the component
|
||||||
Args: {};
|
Args: {};
|
||||||
// Any blocks yielded by the component
|
// Any blocks yielded by the component
|
||||||
|
@ -20,14 +20,15 @@ export interface PasskeySignature {
|
||||||
Element: null;
|
Element: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Auth extends Component<PasskeySignature> {
|
export default class Auth extends Component<AuthSignature> {
|
||||||
@tracked username: string = '';
|
@tracked username: string = '';
|
||||||
@tracked error: string | undefined;
|
@tracked error: string | undefined;
|
||||||
@tracked isLogin = true;
|
//@tracked isLogin = true;
|
||||||
@service declare auth: AuthService;
|
@service declare auth: AuthService;
|
||||||
|
|
||||||
@action async startLogin() {
|
@action async startLogin() {
|
||||||
try {
|
try {
|
||||||
|
// TODO: Check if account exists and is alowed to login
|
||||||
this.auth.startLogin(this.username);
|
this.auth.startLogin(this.username);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.error = 'Error: ' + error.message;
|
this.error = 'Error: ' + error.message;
|
||||||
|
@ -36,7 +37,12 @@ export default class Auth extends Component<PasskeySignature> {
|
||||||
|
|
||||||
@action async startRegistration() {
|
@action async startRegistration() {
|
||||||
try {
|
try {
|
||||||
this.auth.startRegistration(this.username);
|
// TODO: Check if handle is already taken
|
||||||
|
await this.auth.startRegistration(this.username);
|
||||||
|
// After registration, log in immediately to obtain a valid session token
|
||||||
|
// for the "post" registration data, such as email
|
||||||
|
await this.auth.startLogin(this.username);
|
||||||
|
// And after login,
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.error = 'Error: ' + error.message;
|
this.error = 'Error: ' + error.message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<Input
|
<Input
|
||||||
@type="text"
|
@type="text"
|
||||||
@value={{this.username}}
|
@value={{this.username}}
|
||||||
@placeholder="Username"
|
placeholder="Username"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div type="button" class="login-start-button" {{on "click" this.onLoginStart}}>
|
<div type="button" class="login-start-button" {{on "click" this.onLoginStart}}>
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import { action } from '@ember/object'
|
import { action } from '@ember/object';
|
||||||
import Component from '@glimmer/component'
|
import Component from '@glimmer/component';
|
||||||
import { tracked } from '@glimmer/tracking'
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
export interface AuthLoginSignature {
|
export interface AuthLoginSignature {
|
||||||
// The arguments accepted by the component
|
// The arguments accepted by the component
|
||||||
Args: {}
|
Args: {};
|
||||||
// Any blocks yielded by the component
|
// Any blocks yielded by the component
|
||||||
Blocks: {
|
Blocks: {
|
||||||
default: []
|
default: [];
|
||||||
}
|
};
|
||||||
// The element to which `...attributes` is applied in the component template
|
// The element to which `...attributes` is applied in the component template
|
||||||
Element: null
|
Element: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class AuthLogin extends Component<AuthLoginSignature> {
|
export default class AuthLogin extends Component<AuthLoginSignature> {
|
||||||
@tracked username = ''
|
@tracked username = '';
|
||||||
|
|
||||||
@action onLoginStart() {
|
@action onLoginStart() {
|
||||||
console.log('Starting login for username ' + this.username)
|
console.log('Starting login for username ' + this.username);
|
||||||
// Check if username is approved for login
|
// Check if username is approved for login
|
||||||
// If it is, continue with login
|
// If it is, continue with login
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<Util::MailEntry @data={{this.mail}}/>
|
||||||
<div class="registration-form-description-wrapper">
|
<div class="registration-form-description-wrapper">
|
||||||
<label>
|
<label>
|
||||||
Description
|
Description
|
||||||
|
@ -20,24 +21,20 @@
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{{!--<div class="registration-form-mail-wrapper">--}}
|
|
||||||
{{!-- <label>--}}
|
|
||||||
{{!-- Email--}}
|
|
||||||
{{!-- <Input @type="text" @value={{this.email}} placeholder="Email address" />--}}
|
|
||||||
{{!-- </label>--}}
|
|
||||||
{{!--</div>--}}
|
|
||||||
<div class="registration-form-gender-wrapper">
|
<div class="registration-form-gender-wrapper">
|
||||||
|
<p class="registration-form-gender-info">Add your preferred pronouns</p>
|
||||||
<Util::StringArray
|
<Util::StringArray
|
||||||
@list={{this.gender}}
|
@list={{this.gender}}
|
||||||
@onNewElement={{this.genderAddedHandler}}
|
@onNewElement={{this.genderAddedHandler}}
|
||||||
@onDeleteElement={{this.genderRemovedHandler}}
|
@onDeleteElement={{this.genderRemovedHandler}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p>{{this.extracted}}</p>
|
|
||||||
<div class="register-form-being-wrapper">
|
<div class="register-form-being-wrapper">
|
||||||
|
<p class="registration-form-being-info">Select the type of being you are. Multiselect is possible</p>
|
||||||
<Util::Multiselect @elements={{this.beingTypes}} />
|
<Util::Multiselect @elements={{this.beingTypes}} />
|
||||||
</div>
|
</div>
|
||||||
<div class="register-form-default-post-mode-wrapper">
|
<div class="register-form-default-post-mode-wrapper">
|
||||||
|
<p class="registration-form-default-post-mode-info">Select the default mode for your posts</p>
|
||||||
<Util::OneOfArray
|
<Util::OneOfArray
|
||||||
@elements={{array "Public" "Local" "Followers" "Direct"}}
|
@elements={{array "Public" "Local" "Followers" "Direct"}}
|
||||||
@selected={{this.defaultpostmode}}
|
@selected={{this.defaultpostmode}}
|
||||||
|
@ -65,4 +62,4 @@
|
||||||
<Util::MapEdit @list={{this.customProperties}} />
|
<Util::MapEdit @list={{this.customProperties}} />
|
||||||
</div>
|
</div>
|
||||||
{{! TODO: Icon, Background, Banner }}
|
{{! TODO: Icon, Background, Banner }}
|
||||||
</div>
|
</div>
|
|
@ -1,27 +1,29 @@
|
||||||
import Component from '@glimmer/component';
|
import { action } from '@ember/object'
|
||||||
import { tracked } from '@glimmer/tracking';
|
import Component from '@glimmer/component'
|
||||||
|
import { tracked } from '@glimmer/tracking'
|
||||||
|
import isValidMail from 'frontend-reactive/helpers/is-valid-mail'
|
||||||
|
|
||||||
export interface AuthPostRegistrationFormSignature {
|
export interface AuthPostRegistrationFormSignature {
|
||||||
// The arguments accepted by the component
|
// The arguments accepted by the component
|
||||||
Args: {
|
Args: {
|
||||||
username: string;
|
username: string
|
||||||
};
|
}
|
||||||
// Any blocks yielded by the component
|
// Any blocks yielded by the component
|
||||||
Blocks: {
|
Blocks: {
|
||||||
default: [];
|
default: []
|
||||||
};
|
}
|
||||||
// The element to which `...attributes` is applied in the component template
|
// The element to which `...attributes` is applied in the component template
|
||||||
Element: null;
|
Element: null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class AuthPostRegistrationForm extends Component<AuthPostRegistrationFormSignature> {
|
export default class AuthPostRegistrationForm extends Component<AuthPostRegistrationFormSignature> {
|
||||||
@tracked displayname: string = this.args.username;
|
@tracked displayname: string = this.args.username
|
||||||
@tracked description: string = '';
|
@tracked description: string = ''
|
||||||
@tracked gender: Array<{ value: string }> = [];
|
@tracked gender: Array<{ value: string }> = []
|
||||||
@tracked beingTypes: Array<{
|
@tracked beingTypes: Array<{
|
||||||
name: string;
|
name: string
|
||||||
checked: boolean;
|
checked: boolean
|
||||||
description: string;
|
description: string
|
||||||
}> = [
|
}> = [
|
||||||
{
|
{
|
||||||
name: 'Human',
|
name: 'Human',
|
||||||
|
@ -53,17 +55,22 @@ export default class AuthPostRegistrationForm extends Component<AuthPostRegistra
|
||||||
description: 'Doll',
|
description: 'Doll',
|
||||||
checked: false,
|
checked: false,
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
@tracked defaultpostmode: string = 'public';
|
@tracked defaultpostmode: string = 'Public'
|
||||||
@tracked followapproval: boolean = false;
|
@tracked followapproval: boolean = false
|
||||||
// Actual custom properties stored in here
|
// Actual custom properties stored in here
|
||||||
@tracked customProperties: Array<{ key: string; value: string }> = [];
|
@tracked customProperties: Array<{ key: string; value: string }> = []
|
||||||
@tracked indexable: boolean = true;
|
@tracked indexable: boolean = true
|
||||||
|
@tracked mail = { mail: '', valid: false }
|
||||||
|
|
||||||
genderAddedHandler(newIndex: number) {
|
genderAddedHandler(newIndex: number) {
|
||||||
console.log('gender added');
|
console.log('gender added')
|
||||||
}
|
}
|
||||||
genderRemovedHandler(removedIndex: number) {
|
genderRemovedHandler(removedIndex: number) {
|
||||||
console.log('gender removed');
|
console.log('gender removed')
|
||||||
|
}
|
||||||
|
|
||||||
|
@action test() {
|
||||||
|
console.log(this.mail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
frontend-reactive/app/components/util/mail-entry.hbs
Normal file
11
frontend-reactive/app/components/util/mail-entry.hbs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="mail-entry {{@wrapper-classes}}">
|
||||||
|
<label>
|
||||||
|
Email
|
||||||
|
<Input @type="text" @value={{this.args.data.mail}} placeholder="Email address" {{on "change" (fn this.checkMail)}}/>
|
||||||
|
</label>
|
||||||
|
{{#if this.mailOk}}
|
||||||
|
<p class="mail-ok">O</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="mail-error">X</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
27
frontend-reactive/app/components/util/mail-entry.ts
Normal file
27
frontend-reactive/app/components/util/mail-entry.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { action } from '@ember/object'
|
||||||
|
import { map } from '@ember/object/computed'
|
||||||
|
import Component from '@glimmer/component'
|
||||||
|
import { tracked } from '@glimmer/tracking'
|
||||||
|
|
||||||
|
const re = /.+@\S+\.\S+/
|
||||||
|
|
||||||
|
export interface UtilMailEntrySignature {
|
||||||
|
// The arguments accepted by the component
|
||||||
|
Args: {
|
||||||
|
data: { mail: string; valid: boolean }
|
||||||
|
}
|
||||||
|
// Any blocks yielded by the component
|
||||||
|
Blocks: {
|
||||||
|
default: []
|
||||||
|
}
|
||||||
|
// The element to which `...attributes` is applied in the component template
|
||||||
|
Element: null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class UtilMailEntry extends Component<UtilMailEntrySignature> {
|
||||||
|
@tracked mailOk = this.args.data.valid
|
||||||
|
@action checkMail() {
|
||||||
|
this.args.data.valid = re.test(this.args.data.mail)
|
||||||
|
this.mailOk = this.args.data.valid
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="{{@wrapper-class}}">
|
<div class="{{@wrapper-class}}">
|
||||||
{{#each @elements as |element|}}
|
{{#each @elements as |element index|}}
|
||||||
<RadioButton
|
<RadioButton
|
||||||
@value="{{element}}"
|
@value="{{element}}"
|
||||||
@groupValue={{@selected}}
|
@groupValue={{@selected}}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { helper } from '@ember/component/helper';
|
import { helper } from '@ember/component/helper'
|
||||||
|
|
||||||
export default helper(function equals(args) {
|
export default helper(function equals(args) {
|
||||||
if (args.length != 2) return false;
|
if (args.length != 2) return false
|
||||||
return args[0] == args[1];
|
console.log(args[0], args[1])
|
||||||
});
|
return args[0] == args[1]
|
||||||
|
})
|
||||||
|
|
14
frontend-reactive/app/helpers/is-valid-mail.ts
Normal file
14
frontend-reactive/app/helpers/is-valid-mail.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { helper } from '@ember/component/helper';
|
||||||
|
|
||||||
|
const re = /.+@\S+\.\S+/;
|
||||||
|
|
||||||
|
// Helper to check if a given email is *probably* valid
|
||||||
|
// Ofc, the only surefire way to check if an email exists is to send a test mail to it.
|
||||||
|
// This sending is expensive however, and thus some mostly sane defaults can be checked for
|
||||||
|
// beforehand. "Bananentürkis" for example is obviously not a valid address
|
||||||
|
export default helper(function isValidMail(positional: string[] /*, named*/) {
|
||||||
|
for (const mail of positional) {
|
||||||
|
if (!re.test(mail)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
|
@ -9,4 +9,6 @@ export default class Router extends EmberRouter {
|
||||||
Router.map(function () {
|
Router.map(function () {
|
||||||
this.route('about');
|
this.route('about');
|
||||||
this.route('registerform');
|
this.route('registerform');
|
||||||
|
this.route('auth');
|
||||||
|
this.route('testing');
|
||||||
});
|
});
|
||||||
|
|
4
frontend-reactive/app/routes/auth.ts
Normal file
4
frontend-reactive/app/routes/auth.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
|
export default class AuthRoute extends Route {}
|
3
frontend-reactive/app/routes/testing.ts
Normal file
3
frontend-reactive/app/routes/testing.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
|
||||||
|
export default class TestingRoute extends Route {}
|
|
@ -109,6 +109,27 @@ export default class AuthService extends Service {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if a given username exist on the server
|
||||||
|
// Note: The server enforces this check itself during both registration and login
|
||||||
|
// but provides the information too so that frontends can provide a better UX
|
||||||
|
async doesUsernameExist(username: string): Promise<boolean> {
|
||||||
|
// TODO: Make API call to check if username/handle is already in use
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a given username is allowed to log in.
|
||||||
|
// This includes a check for the existence of the username in the first place
|
||||||
|
// A username may not log in for various reasons, two of which are the account not being approved yet
|
||||||
|
// or the account being barred login from an admin
|
||||||
|
// Note: The server enforces this check itself during login. However, it also provides an API endpoint
|
||||||
|
// for performing this check to allow frontends to have a better UX
|
||||||
|
async canUsernameLogin(username: string): Promise<boolean> {
|
||||||
|
// Can't login into a non-existing account
|
||||||
|
if (!(await this.doesUsernameExist(username))) return false;
|
||||||
|
// TODO: Make API call to check if username is allowed to login
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't remove this declaration: this is what enables TypeScript to resolve
|
// Don't remove this declaration: this is what enables TypeScript to resolve
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
/* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */
|
/* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */
|
||||||
|
|
||||||
|
/* Note: CSS is fucking stupid. It applies styles not in the order classes are set on an element,
|
||||||
|
* but in the order they appear in the css files */
|
||||||
|
|
||||||
/* @import url("debug.css"); */
|
/* @import url("debug.css"); */
|
||||||
@import url("fonts.css");
|
@import url("fonts.css");
|
||||||
@import url("colors.css");
|
@import url("colors.css");
|
||||||
@import url("notes.css");
|
|
||||||
@import url("util.css");
|
@import url("util.css");
|
||||||
|
@import url("util/stringArray.css");
|
||||||
|
@import url("util/mailEntry.css");
|
||||||
@import url("svgs.css");
|
@import url("svgs.css");
|
||||||
|
@import url("notes.css");
|
||||||
@import url("timeline.css");
|
@import url("timeline.css");
|
||||||
@import url("auth.css");
|
@import url("auth.css");
|
||||||
@import url("stringArray.css");
|
@import url("auth/registerForm.css");
|
||||||
|
|
3
frontend-reactive/app/styles/auth/registerForm.css
Normal file
3
frontend-reactive/app/styles/auth/registerForm.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.registration-form {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
6
frontend-reactive/app/styles/util/mailEntry.css
Normal file
6
frontend-reactive/app/styles/util/mailEntry.css
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.mail-entry {
|
||||||
|
}
|
||||||
|
.mail-ok {
|
||||||
|
}
|
||||||
|
.mail-ok {
|
||||||
|
}
|
2
frontend-reactive/app/templates/auth.hbs
Normal file
2
frontend-reactive/app/templates/auth.hbs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{{page-title "Auth"}}
|
||||||
|
{{outlet}}
|
|
@ -1,4 +1,4 @@
|
||||||
{{page-title "RegisterForm"}}
|
{{page-title "RegisterForm"}}
|
||||||
<Auth::Login />
|
<Auth::Login />
|
||||||
<br>
|
<br>
|
||||||
<!--<Auth::PostRegistrationForm @username="bob" />-->
|
{{!--<Auth::PostRegistrationForm @username="bob" />--}}
|
2
frontend-reactive/app/templates/testing.hbs
Normal file
2
frontend-reactive/app/templates/testing.hbs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{{page-title "Testing"}}
|
||||||
|
<Auth::PostRegistrationForm @username="bob" />
|
|
@ -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/mail-entry', 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`<Util::MailEntry />`);
|
||||||
|
|
||||||
|
assert.dom().hasText('');
|
||||||
|
|
||||||
|
// Template block usage:
|
||||||
|
await render(hbs`
|
||||||
|
<Util::MailEntry>
|
||||||
|
template block text
|
||||||
|
</Util::MailEntry>
|
||||||
|
`);
|
||||||
|
|
||||||
|
assert.dom().hasText('template block text');
|
||||||
|
});
|
||||||
|
});
|
|
@ -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 | isValidMail', function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
// TODO: Replace this with your real tests.
|
||||||
|
test('it renders', async function (assert) {
|
||||||
|
this.set('inputValue', '1234');
|
||||||
|
|
||||||
|
await render(hbs`{{is-valid-mail this.inputValue}}`);
|
||||||
|
|
||||||
|
assert.dom().hasText('1234');
|
||||||
|
});
|
||||||
|
});
|
11
frontend-reactive/tests/unit/routes/auth-test.ts
Normal file
11
frontend-reactive/tests/unit/routes/auth-test.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'frontend-reactive/tests/helpers';
|
||||||
|
|
||||||
|
module('Unit | Route | auth', function (hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
test('it exists', function (assert) {
|
||||||
|
const route = this.owner.lookup('route:auth');
|
||||||
|
assert.ok(route);
|
||||||
|
});
|
||||||
|
});
|
11
frontend-reactive/tests/unit/routes/testing-test.ts
Normal file
11
frontend-reactive/tests/unit/routes/testing-test.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'frontend-reactive/tests/helpers';
|
||||||
|
|
||||||
|
module('Unit | Route | testing', function (hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
test('it exists', function (assert) {
|
||||||
|
const route = this.owner.lookup('route:testing');
|
||||||
|
assert.ok(route);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue