diff --git a/addons/core/public/resource-creation.svg b/addons/core/public/resource-creation.svg deleted file mode 100644 index 8f85dd1a8..000000000 --- a/addons/core/public/resource-creation.svg +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/core/public/ssh-target-creation.svg b/addons/core/public/ssh-target-creation.svg new file mode 100644 index 000000000..bce6c57f9 --- /dev/null +++ b/addons/core/public/ssh-target-creation.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/core/tests/integration/components/image-test.js b/addons/core/tests/integration/components/image-test.js index f5a7c4586..efd6e21be 100644 --- a/addons/core/tests/integration/components/image-test.js +++ b/addons/core/tests/integration/components/image-test.js @@ -8,7 +8,7 @@ module('Integration | Component | Image', function (hooks) { test('it renders', async function (assert) { assert.expect(1); - await render(hbs``); + await render(hbs``); assert.ok(find('.image-container')); }); }); diff --git a/addons/core/translations/actions/en-us.yaml b/addons/core/translations/actions/en-us.yaml index 79537e28b..ab03c58db 100644 --- a/addons/core/translations/actions/en-us.yaml +++ b/addons/core/translations/actions/en-us.yaml @@ -41,3 +41,4 @@ clear-all-filters: Clear all filters learn-more: Learn more refresh: Refresh remove-resources: Remove resources and save +do-this-later: Do this later diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml index 35787d6d8..83b880c0b 100644 --- a/addons/core/translations/en-us.yaml +++ b/addons/core/translations/en-us.yaml @@ -53,24 +53,17 @@ themes: dark: Dark onboarding: title: Onboarding - quick-start: + add-target: titles: - choose-path: Welcome to Boundary - quick-setup: Quick setup - manual: Manual - create-resource: Creating your first target - resource-setup-info: We’ll set up scopes & a host catalog for you - host-address: Host Address - target-port: Target Port - toggle-resource-info: I have an IP address and port for my first host + have-a-target: Have a target to test? + target-address: Target Address + target-port: Default Port + target-setup-info: We’ll set up scopes for you descriptions: - choose-path: You’ve taken the first step towards making your infrastructure more secure. How do you want to get started? - quick-setup: I want to see how Boundary works, help me set up my first scopes and targets. - manual: I want to set up my first scopes and targets myself, starting with my first org. - create-resource: In Boundary, a “target” defines a server that your users will connect to. - resource-setup-info: Hosts are organized in host catalogs, which live in projects, which live in orgs. We will create these for you, and they can be edited or deleted later. - host-address: 'Example: “192.168.0.1” or IPv6' - target-port: 'Example: “22”' + setup-first-target: In Boundary, a “target” defines a server that your users will connect to. We can help you set up your first target for proof-of-concept testing, or you can skip this step if you don’t want to do it right now. + target-address: Host name or IP address. + target-port: The default port on which to connect. + target-setup-info: Targets live in projects, and projects live in orgs. We will create these example scopes for you but they can be edited or deleted later. success: titles: connect: Connect to your target diff --git a/addons/core/translations/errors/en-us.yaml b/addons/core/translations/errors/en-us.yaml index 2d8ff5dc3..dc85956c2 100644 --- a/addons/core/translations/errors/en-us.yaml +++ b/addons/core/translations/errors/en-us.yaml @@ -19,5 +19,6 @@ authentication-failed: title: Authentication Failed cluster-url-verification-failed: description: Unable to connect to the specified cluster URL. Please check the URL and try again. -quick-setup-failed: - description: We're sorry, quick setup was unable to generate all resources. +onboarding-failed: + description: We're sorry, onboarding was unable to generate a TCP target. +required-field: This is a required field. diff --git a/ui/admin/app/components/form/onboarding/choose-path/index.hbs b/ui/admin/app/components/form/onboarding/choose-path/index.hbs deleted file mode 100644 index 1c4a59b96..000000000 --- a/ui/admin/app/components/form/onboarding/choose-path/index.hbs +++ /dev/null @@ -1,37 +0,0 @@ - - - - - {{t 'onboarding.quick-start.titles.quick-setup'}} - {{t - 'onboarding.quick-start.descriptions.quick-setup' - }} - - - {{t 'onboarding.quick-start.titles.manual'}} - {{t - 'onboarding.quick-start.descriptions.manual' - }} - - - - - \ No newline at end of file diff --git a/ui/admin/app/components/form/onboarding/choose-path/index.js b/ui/admin/app/components/form/onboarding/choose-path/index.js deleted file mode 100644 index d8e725ba2..000000000 --- a/ui/admin/app/components/form/onboarding/choose-path/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import { action } from '@ember/object'; - -export default class FormOnboardingChoosePathComponent extends Component { - // =properties - - /** - * Onboarding path property - * @type {string} - */ - @tracked path = 'guided'; - - // =actions - - @action - toggleRadio(path) { - this.path = path; - } -} diff --git a/ui/admin/app/components/form/onboarding/create-resources/index.hbs b/ui/admin/app/components/form/onboarding/create-resources/index.hbs deleted file mode 100644 index c2646a8e2..000000000 --- a/ui/admin/app/components/form/onboarding/create-resources/index.hbs +++ /dev/null @@ -1,46 +0,0 @@ - - - - {{#if this.showHostInfo}} -
- - - -
- {{/if}} - - <:title>{{t 'onboarding.quick-start.titles.resource-setup-info'}} - <:description>{{t - 'onboarding.quick-start.descriptions.resource-setup-info' - }} - - - <:body> - - - - - - -
\ No newline at end of file diff --git a/ui/admin/app/components/form/onboarding/create-resources/index.js b/ui/admin/app/components/form/onboarding/create-resources/index.js deleted file mode 100644 index 2f71b3aea..000000000 --- a/ui/admin/app/components/form/onboarding/create-resources/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import { tracked } from '@glimmer/tracking'; - -export default class FormOnboardingComponent extends Component { - // =actions - @tracked showHostInfo = false; - - @action - toggleHostInfoSetup() { - this.showHostInfo = !this.showHostInfo; - } -} diff --git a/ui/admin/app/components/form/onboarding/index.hbs b/ui/admin/app/components/form/onboarding/index.hbs new file mode 100644 index 000000000..92ef4f616 --- /dev/null +++ b/ui/admin/app/components/form/onboarding/index.hbs @@ -0,0 +1,63 @@ + +
+ + {{t 'onboarding.add-target.titles.target-address'}} + + {{t 'onboarding.add-target.descriptions.target-address'}} + + + {{#if this.targetAddressInvalid}} + + {{t 'errors.required-field'}} + + {{/if}} + + + + {{t 'onboarding.add-target.titles.target-port'}} + + {{t 'onboarding.add-target.descriptions.target-port'}} + + {{#if this.targetPortInvalid}} + + {{t 'errors.required-field'}} + + {{/if}} + +
+ + + {{t 'onboarding.add-target.titles.target-setup-info'}} + {{t + 'onboarding.add-target.descriptions.target-setup-info' + }} + + + + + + + + +
\ No newline at end of file diff --git a/ui/admin/app/components/form/onboarding/index.js b/ui/admin/app/components/form/onboarding/index.js new file mode 100644 index 000000000..373758923 --- /dev/null +++ b/ui/admin/app/components/form/onboarding/index.js @@ -0,0 +1,23 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class FormOnboardingComponent extends Component { + // =properties + @tracked targetAddress; + @tracked targetAddressInvalid = false; + @tracked targetPort; + @tracked targetPortInvalid = false; + + // =actions + @action + submit() { + // Check targetAddress and targetPort are valid before submit + this.targetAddressInvalid = !this.targetAddress; + this.targetPortInvalid = !this.targetPort; + if (!this.targetAddressInvalid && !this.targetPortInvalid) { + // Call submit fn passed to the form + this.args.submit(this.targetAddress, this.targetPort); + } + } +} diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js index b3865f84b..f7f67698f 100644 --- a/ui/admin/app/router.js +++ b/ui/admin/app/router.js @@ -140,11 +140,6 @@ Router.map(function () { }); this.route('onboarding', function () { - this.route('quick-setup', function () { - this.route('choose-path'); - this.route('create-resources', function () { - this.route('success'); - }); - }); + this.route('success'); }); }); diff --git a/ui/admin/app/routes/onboarding.js b/ui/admin/app/routes/onboarding.js index 74acfb6e5..68b52bf73 100644 --- a/ui/admin/app/routes/onboarding.js +++ b/ui/admin/app/routes/onboarding.js @@ -1,36 +1,146 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; - +import { action } from '@ember/object'; +import { loading } from 'ember-loading'; +import { notifyError } from 'core/decorators/notify'; +import { TYPE_TARGET_TCP } from 'api/models/target'; export default class OnboardingRoute extends Route { // =services @service store; @service session; @service router; + @service intl; // =methods /** - * If arriving here unauthenticated, redirect to index for further processing. + * If arriving here unauthenticated, or has orgs redirect to index for further processing. */ - beforeModel() { - if (!this.session.isAuthenticated) this.router.transitionTo('index'); + async beforeModel() { + const orgs = await this.store.query('scope', { + scope_id: 'global', + }); + if (!this.session.isAuthenticated || orgs.length) + this.router.transitionTo('index'); } /** * List all scopes (orgs) under global. + * Creare org, project, target and roles */ - model() { - return this.store.query('scope', { - scope_id: 'global', - }); + async model() { + return { + org: this.store.createRecord('scope', { + type: 'org', + scopeID: 'global', + name: 'SecOps', + description: 'Sample org created by quick setup', + }), + project: this.store.createRecord('scope', { + type: 'project', + name: 'AWS', + description: 'Sample project created by quick setup', + }), + target: this.store.createRecord('target', { + type: TYPE_TARGET_TCP, + name: 'Test target', + description: 'Sample target created by quick setup', + }), + role: this.store.createRecord('role', { + name: 'test_target_role', + }), + }; + } + + // =actions + @action + @loading + @notifyError(function () { + return this.intl.t('errors.onboarding-failed.description'); + }) + async createResources(targetAddress, targetPort) { + const { org, project, target, role } = this.currentModel; + + await this.createOrg(org); + await this.createProject(org, project); + await this.createTarget(project, target, targetAddress, targetPort); + await this.createGrants(org, project, target, role); + + // Redirect to success + this.router.transitionTo('onboarding.success'); } /** - * If any orgs already exist, redirect to index to skip onboarding. + * Creates Org, if fails, redirects to Orgs empty list + * @param {Object} org */ - redirect(model) { - // If there are orgs already created, redirect to index. - if (model.length) this.router.transitionTo('index'); + async createOrg(org) { + try { + await org.save(); + } catch (error) { + // Redirects to Orgs screen + this.router.transitionTo('scopes.scope', 'global'); + throw new Error(error); + } + } + + /** + * Creates Project, if fails redirects to Projects from Org empty list. + * @param {Object} org + * @param {Object} project + */ + async createProject(org, project) { + project.scopeID = org.id; + + try { + await project.save(); + } catch (error) { + // Redirects to Project screen + this.router.transitionTo('scopes.scope.scopes', project.scopeID); + throw new Error(error); + } + } + + /** + * Creates a Target, if fails redirects to Targets empyt list. + * @param {Object} project + * @param {Object} target + * @param {Object} targetAddress + * @param {Object} targetPort + */ + async createTarget(project, target, targetAddress, targetPort) { + target.scopeID = project.id; + target.address = targetAddress; + target.default_port = targetPort; + + try { + await target.save(); + } catch (error) { + // Redirect to targets global + this.router.replaceWith('scopes.scope.targets', project.scopeID); + throw new Error(error); + } + } + + async createGrants(org, project, target, role) { + role.scopeID = org.id; + role.grant_scope_id = project.id; + try { + await role.save(); + await role.saveGrantStrings([ + `type=target;actions=list`, + `id=${target.id};actions=authorize-session`, + ]); + } catch (error) { + // Redirect to targets that belong to scope + this.router.replaceWith('scopes.scope.targets', project.scopeID); + throw new Error(error); + } + } + + @action + async doLater() { + this.router.transitionTo('index'); } } diff --git a/ui/admin/app/routes/onboarding/quick-setup/choose-path.js b/ui/admin/app/routes/onboarding/quick-setup/choose-path.js deleted file mode 100644 index f953c18e0..000000000 --- a/ui/admin/app/routes/onboarding/quick-setup/choose-path.js +++ /dev/null @@ -1,24 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OnboardingQuickSetupChoosePathRoute extends Route { - // =services - - @service router; - - // =actions - - @action - choosePath(path = 'guided') { - switch (path) { - case 'guided': - this.router.transitionTo('onboarding.quick-setup.create-resources'); - break; - case 'manual': - default: - this.router.transitionTo('scopes.scope.scopes.new', 'global'); - break; - } - } -} diff --git a/ui/admin/app/routes/onboarding/quick-setup/create-resources.js b/ui/admin/app/routes/onboarding/quick-setup/create-resources.js deleted file mode 100644 index cc9f540ea..000000000 --- a/ui/admin/app/routes/onboarding/quick-setup/create-resources.js +++ /dev/null @@ -1,108 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; -import { loading } from 'ember-loading'; -import { notifyError } from 'core/decorators/notify'; -import { TYPE_TARGET_TCP } from 'api/models/target'; - -export default class OnboardingQuickSetupCreateResourcesRoute extends Route { - // =services - - @service store; - @service router; - @service intl; - - // =methods - - async model() { - return { - org: this.store.createRecord('scope', { - type: 'org', - scopeID: 'global', - name: 'SecOps', - description: 'Sample org created by quick setup', - }), - project: this.store.createRecord('scope', { - type: 'project', - name: 'AWS', - description: 'Sample project created by quick setup', - }), - hostCatalog: this.store.createRecord('host-catalog', { - type: 'static', - name: 'US-East-1', - description: 'Sample host catalog created by quick setup', - }), - hostSet: this.store.createRecord('host-set', { - name: 'Prod-EC2', - description: 'Sample host set created by quick setup', - }), - host: this.store.createRecord('host', { - name: 'Instance-1', - description: 'Sample host created by quick setup', - }), - target: this.store.createRecord('target', { - type: TYPE_TARGET_TCP, - name: 'EC2 Instances', - description: 'Sample target created by quick setup', - }), - role: this.store.createRecord('role', { - name: 'ec2_connect_role', - }), - }; - } - - // =actions - - @action - @loading - @notifyError(function () { - return this.intl.t('errors.quick-setup-failed.description'); - }) - async createResources(hostAddress, targetPort) { - try { - await this.createOnboardingResourcesAndRedirect(hostAddress, targetPort); - } catch (e) { - this.router.replaceWith('scopes.scope', 'global'); - throw new Error(); - } - } - - async createOnboardingResourcesAndRedirect(hostAddress, targetPort) { - const { org, project, hostCatalog, hostSet, host, target, role } = - this.currentModel; - // The Procedure - await org.save(); - project.scopeID = org.id; - await project.save(); - hostCatalog.scopeID = project.id; - await hostCatalog.save(); - hostSet.host_catalog_id = hostCatalog.id; - await hostSet.save(); - host.host_catalog_id = hostCatalog.id; - if (hostAddress) { - host.address = hostAddress; - await host.save(); - await hostSet.addHosts([host.id]); - target.scopeID = project.id; - target.default_port = targetPort; - await target.save(); - await target.addHostSources([hostSet.id]); - role.scopeID = org.id; - role.grant_scope_id = project.id; - await role.save(); - await role.saveGrantStrings([ - `type=target;actions=list`, - `id=${target.id};actions=authorize-session`, - ]); - this.router.transitionTo( - 'onboarding.quick-setup.create-resources.success' - ); - } else { - this.router.transitionTo( - 'scopes.scope.host-catalogs.host-catalog.hosts', - project.id, - hostCatalog.id - ); - } - } -} diff --git a/ui/admin/app/routes/onboarding/quick-setup/create-resources/success.js b/ui/admin/app/routes/onboarding/quick-setup/create-resources/success.js deleted file mode 100644 index 19cb8f862..000000000 --- a/ui/admin/app/routes/onboarding/quick-setup/create-resources/success.js +++ /dev/null @@ -1,38 +0,0 @@ -import Route from '@ember/routing/route'; -import { getOwner } from '@ember/application'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OnboardingQuickSetupCreateResourcesSuccessRoute extends Route { - // =services - @service router; - - // =attributes - /** - * Looks up the window object indirectly. - * @type {Window} - */ - get window() { - // The Ember way of accessing globals... - const document = getOwner(this).lookup('service:-document').documentElement; - // defaultView === window, but without using globals directly - return document.parentNode.defaultView; - } - // =methods - model() { - const model = this.modelFor('onboarding.quick-setup.create-resources'); - return { - ...model, - clusterUrl: this.window.location.origin, - }; - } - - @action - showTargetList(scope) { - const { - project: { id: scopeID }, - target: { id: targetID }, - } = scope; - this.router.transitionTo('scopes.scope.targets.target', scopeID, targetID); - } -} diff --git a/ui/admin/app/routes/onboarding/success.js b/ui/admin/app/routes/onboarding/success.js new file mode 100644 index 000000000..831108d0b --- /dev/null +++ b/ui/admin/app/routes/onboarding/success.js @@ -0,0 +1,28 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class OnboardingSuccessRoute extends Route { + // =services + @service router; + @service session; + @service browserObject; + + // =methods + model() { + const model = this.modelFor('onboarding'); + return { + ...model, + clusterUrl: this.browserObject.window.origin, + }; + } + + @action + showTargetList(scope) { + const { + project: { id: scopeID }, + target: { id: targetID }, + } = scope; + this.router.transitionTo('scopes.scope.targets.target', scopeID, targetID); + } +} diff --git a/ui/admin/app/routes/scopes/scope/index.js b/ui/admin/app/routes/scopes/scope/index.js index e68f02836..91ee09cb0 100644 --- a/ui/admin/app/routes/scopes/scope/index.js +++ b/ui/admin/app/routes/scopes/scope/index.js @@ -27,7 +27,7 @@ export default class ScopesScopeIndexRoute extends Route { // If authenticated but no orgs exist yet, redirect to onboarding if (this.session.isAuthenticated && !hasOrgs) { - return this.router.transitionTo('onboarding.quick-setup.choose-path'); + return this.router.transitionTo('onboarding'); } // If this a project scope, there are no further sub scopes, so show edit diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 29aae66e9..ed4254386 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -238,161 +238,90 @@ } } -// onboarding quick setup styles -.choose-path { - display: block; - width: sizing.rems(xl) * 15; -} - -.choose-path-cards { - display: flex; - justify-content: space-between; -} - +// Onboarding styles .create-resources { - display: block; + display: flex; + align-items: flex-start; width: sizing.rems(xl) * 15; - .rose-form-fieldset { - margin-top: sizing.rems(xl); - } - - .rose-frame-body { - p { - color: var(--ui-gray); - } - } -} - -.create-resource-cards { - display: flex; - justify-content: space-between; - - .rose-form-input { - width: sizing.rems(xl) * 6; - } -} - -.onboarding-quick-setup-success { - display: block; - width: min-content; - - .rose-frame-header { - padding: sizing.rems(l); - } - - h2 { - font-weight: 700; - line-height: 24px; - margin: 0; - } - - h4 { - font-weight: 700; - line-height: 19px; - } - - a { - font-size: 12px; - font-weight: 600; - line-height: 16px; - } -} - -.onboarding-quick-setup-success-role { - span { - display: flex; - } - - h3 { - font-size: 16px; - font-weight: 600; - letter-spacing: -0.5px; - line-height: 24px; - margin-left: sizing.rems(xs); - margin-top: 0; - } - - .rose-icon { - color: var(--success); - } -} - -.onboarding-quick-setup-success-sub-title { - display: inline-flex; - - h3 { - font-weight: 600; - letter-spacing: -0.5px; - line-height: 24px; - margin-left: sizing.rems(xs); - margin-top: sizing.rems(xxs); - } - - .rose-icon { - margin-bottom: sizing.rems(xs); - } -} - -.onboarding-quick-setup-success-link { - display: block; - margin-top: sizing.rems(xs); - text-decoration: none; -} - -.onboarding-quick-setup-success-image { - margin-bottom: sizing.rems(xl); - margin-top: sizing.rems(l); -} - -.onboarding-quick-setup-success-button { - display: flex; - justify-content: center; - margin-bottom: 0; - margin-top: sizing.rems(xl); - width: 100%; -} - -.onboarding-quick-setup-success-controller { - margin-top: sizing.rems(l); - - h4 { - font-weight: 600; - line-height: 18px; - margin-bottom: sizing.rems(xxs); - } - - p { - color: var(--ui-gray); - margin-bottom: sizing.rems(xxs); - } -} - -.onboarding-quick-setup-success-controller-url { - border: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2); - border-radius: sizing.rems(xxxs); - color: var(--action); - height: (sizing.rems(xs) * 5); - - .copyable { + .create-resources-form { display: flex; justify-content: space-between; - padding: sizing.rems(s); + gap: sizing.rems(s); } - .rose-icon { - margin: 0; + .hds-form-group { + margin-top: sizing.rems(l); + justify-content: center; + + .image-container { + display: flex; + justify-content: center; + margin-top: sizing.rems(l); + } } - .copyable-button { - top: -4px; - } -} + .create-resources-item { + margin-bottom: sizing.rems(xxl); -.onboarding-quick-setup-success-developers { - p { - margin-bottom: sizing.rems(s); - margin-top: sizing.rems(xs); + .create-resources-item-header { + display: flex; + align-items: center; + align-self: center; + gap: 8px; + margin-bottom: sizing.rems(s); + + h3 { + margin: 0; + } + + .hds-icon-tile { + color: var(--token-color-foreground-success); + } + } + + .hds-link-standalone { + margin-bottom: sizing.rems(xs); + } + + .create-resources-item-controller { + margin-top: sizing.rems(xl); + + .create-resources-item-controller-label { + color: var(--ui-gray); + } + + .create-resources-item-controller-copyable { + display: flex; + flex-direction: column; + flex-wrap: wrap; + justify-content: center; + border: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2); + border-radius: sizing.rems(xxxs); + color: var(--action); + height: (sizing.rems(xs) * 5); + margin: sizing.rems(xxs) 0 sizing.rems(l) 0; + + .copyable { + display: flex; + justify-content: space-between; + padding: sizing.rems(s); + + span { + display: flex; + } + } + + .copyable-button { + top: 0; // This is to override a negative top declared above + } + } + + .image-container > svg { + width: 100%; + height: 100%; + } + } } } diff --git a/ui/admin/app/templates/onboarding/index.hbs b/ui/admin/app/templates/onboarding/index.hbs new file mode 100644 index 000000000..1f8e79663 --- /dev/null +++ b/ui/admin/app/templates/onboarding/index.hbs @@ -0,0 +1,15 @@ + + + <:header> +

{{t 'onboarding.add-target.titles.have-a-target'}}

+ + <:body> +

{{t 'onboarding.add-target.descriptions.setup-first-target'}}

+ + +
+
\ No newline at end of file diff --git a/ui/admin/app/templates/onboarding/quick-setup/choose-path.hbs b/ui/admin/app/templates/onboarding/quick-setup/choose-path.hbs deleted file mode 100644 index 88e02acd5..000000000 --- a/ui/admin/app/templates/onboarding/quick-setup/choose-path.hbs +++ /dev/null @@ -1,11 +0,0 @@ - - - <:header> -

{{t 'onboarding.quick-start.titles.choose-path'}}

- - <:body> -

{{t 'onboarding.quick-start.descriptions.choose-path'}}

- - -
-
\ No newline at end of file diff --git a/ui/admin/app/templates/onboarding/quick-setup/create-resources/index.hbs b/ui/admin/app/templates/onboarding/quick-setup/create-resources/index.hbs deleted file mode 100644 index 388fce16e..000000000 --- a/ui/admin/app/templates/onboarding/quick-setup/create-resources/index.hbs +++ /dev/null @@ -1,15 +0,0 @@ -{{page-title (t 'titles.create-resources')}} - - - <:header> -

{{t 'onboarding.quick-start.titles.quick-setup'}}

- - <:body> -

{{t 'onboarding.quick-start.titles.create-resource'}}

-

{{t 'onboarding.quick-start.descriptions.create-resource'}}

- - -
-
\ No newline at end of file diff --git a/ui/admin/app/templates/onboarding/quick-setup/create-resources/success.hbs b/ui/admin/app/templates/onboarding/quick-setup/create-resources/success.hbs deleted file mode 100644 index 65e342d12..000000000 --- a/ui/admin/app/templates/onboarding/quick-setup/create-resources/success.hbs +++ /dev/null @@ -1,98 +0,0 @@ -{{page-title (t 'titles.success')}} - - - <:header> -

{{t 'onboarding.success.titles.connect'}}

- - <:body> -
- - -

- {{t 'onboarding.success.titles.creation-success'}} -

-
-

{{t 'onboarding.success.descriptions.role'}}

-
-
- -

- {{t 'onboarding.success.titles.desktop'}} -

-
- -

{{t 'onboarding.success.descriptions.download'}}

- - - - {{t 'onboarding.success.links.install'}} - - - - - {{t 'onboarding.success.links.learn'}} - - -
-

{{t 'onboarding.success.titles.controller'}}

-

{{t 'onboarding.success.descriptions.controller'}}

-
- - {{this.model.clusterUrl}} - -
-
- -
- -
- -
-

{{t 'onboarding.success.titles.developers'}}

- -

{{t 'onboarding.success.descriptions.developers'}}

- - - - {{t 'onboarding.success.links.cli'}} - - - - - {{t 'onboarding.success.links.api'}} - - - {{t 'actions.done'}} - -
- -
-
\ No newline at end of file diff --git a/ui/admin/app/templates/onboarding/success.hbs b/ui/admin/app/templates/onboarding/success.hbs new file mode 100644 index 000000000..a37346b1d --- /dev/null +++ b/ui/admin/app/templates/onboarding/success.hbs @@ -0,0 +1,94 @@ +{{page-title (t 'titles.success')}} + + + <:header> +

{{t 'onboarding.success.titles.connect'}}

+ + <:body> + {{! We have created }} +
+ + +

{{t 'onboarding.success.titles.creation-success'}}

+
+

{{t 'onboarding.success.descriptions.role'}}

+
+ + {{! Download Desktop client }} +
+ + +

{{t 'onboarding.success.titles.desktop'}}

+
+

{{t 'onboarding.success.descriptions.download'}}

+ + + + + + {{! Controller }} +
+

+ {{t 'onboarding.success.titles.controller'}} +

+ {{t + 'onboarding.success.descriptions.controller' + }} +
+ + {{this.model.clusterUrl}} + +
+ + +
+
+ + {{! Developers }} +
+

+ {{t 'onboarding.success.titles.developers'}} +

+

{{t 'onboarding.success.descriptions.developers'}}

+ + + + +
+ + + +
+
\ No newline at end of file diff --git a/ui/admin/tests/acceptance/onboarding/choose-path-test.js b/ui/admin/tests/acceptance/onboarding/choose-path-test.js deleted file mode 100644 index 836892631..000000000 --- a/ui/admin/tests/acceptance/onboarding/choose-path-test.js +++ /dev/null @@ -1,50 +0,0 @@ -import { module, test } from 'qunit'; -import { visit, currentURL, find, click } from '@ember/test-helpers'; -import { setupApplicationTest } from 'ember-qunit'; -import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; -import { authenticateSession } from 'ember-simple-auth/test-support'; - -module('Acceptance | onboarding | choose-path', function (hooks) { - setupApplicationTest(hooks); - setupMirage(hooks); - - const urls = { - choosePath: '/onboarding/quick-setup/choose-path', - newOrg: '/scopes/global/scopes/new', - createResources: '/onboarding/quick-setup/create-resources', - }; - - hooks.beforeEach(function () { - authenticateSession({}); - }); - - test('defaults to the guided path', async function (assert) { - assert.expect(2); - await visit(urls.choosePath); - assert.strictEqual(find('input[name=path]:checked').value, 'guided'); - assert.false(find('input[value=manual]').checked); - }); - - test('updates to the manual path when manual card clicked', async function (assert) { - assert.expect(2); - await visit(urls.choosePath); - await click('[name="path"][value="manual"]'); - assert.strictEqual(find('input[name=path]:checked').value, 'manual'); - assert.false(find('input[value=guided]').checked); - }); - - test('guided path directs user to /create-resources route', async function (assert) { - assert.expect(1); - await visit(urls.choosePath); - await click('[type="submit"]'); - assert.strictEqual(currentURL(), urls.createResources); - }); - - test('guided path directs user to /global/new route', async function (assert) { - assert.expect(1); - await visit(urls.choosePath); - await click('[name="path"][value="manual"]'); - await click('[type="submit"]'); - assert.strictEqual(currentURL(), urls.newOrg); - }); -}); diff --git a/ui/admin/tests/acceptance/onboarding/create-resource-test.js b/ui/admin/tests/acceptance/onboarding/create-resource-test.js deleted file mode 100644 index f0455507d..000000000 --- a/ui/admin/tests/acceptance/onboarding/create-resource-test.js +++ /dev/null @@ -1,49 +0,0 @@ -import { module, test } from 'qunit'; -import { visit, currentURL, find, click, fillIn } from '@ember/test-helpers'; -import { setupApplicationTest } from 'ember-qunit'; -import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; -import { authenticateSession } from 'ember-simple-auth/test-support'; - -module('Acceptance | onboarding | create-resources', function (hooks) { - setupApplicationTest(hooks); - setupMirage(hooks); - - const urls = { - createResources: '/onboarding/quick-setup/create-resources', - successPath: '/onboarding/quick-setup/create-resources/success', - hostCatalogPath: null, - }; - - hooks.beforeEach(function () { - authenticateSession({}); - }); - - test('show hostAddress and targetPort fields when the checkbox is checked', async function (assert) { - assert.expect(2); - await visit(urls.createResources); - await click('[type="checkbox"]'); - assert.ok(find('[name="hostAddress"]')); - assert.ok(find('[name="targetPort"]')); - }); - - test('redirects user to success screen when next is clicked', async function (assert) { - assert.expect(1); - await visit(urls.createResources); - await click('[type="checkbox"]'); - await fillIn('[name="hostAddress"]', '0.0.0.0'); - await fillIn('[name="targetPort"]', '22'); - await click('[type="submit"]'); - assert.strictEqual(currentURL(), urls.successPath); - }); - - test('redirects user to host catalog screen when next is clicked', async function (assert) { - assert.expect(1); - await visit(urls.createResources); - await click('[type="submit"]'); - const projectId = this.server.schema.scopes.where({ type: 'project' }) - .models[0].id; - const hostCatalogId = this.server.schema.hostCatalogs.all().models[0].id; - urls.hostCatalogPath = `/scopes/${projectId}/host-catalogs/${hostCatalogId}/hosts`; - assert.strictEqual(currentURL(), urls.hostCatalogPath); - }); -}); diff --git a/ui/admin/tests/acceptance/onboarding/onboarding-test.js b/ui/admin/tests/acceptance/onboarding/onboarding-test.js new file mode 100644 index 000000000..223fa4321 --- /dev/null +++ b/ui/admin/tests/acceptance/onboarding/onboarding-test.js @@ -0,0 +1,43 @@ +import { module, test } from 'qunit'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { authenticateSession } from 'ember-simple-auth/test-support'; +import { visit, fillIn, click, currentURL } from '@ember/test-helpers'; + +module('Acceptance | onboarding', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const urls = { + onboarding: '/onboarding', + success: '/onboarding/success', + orgs: '/scopes/global/scopes', + }; + + hooks.beforeEach(() => { + authenticateSession({}); + }); + + test('show targetAddress and targetPort fields', async function (assert) { + assert.expect(2); + await visit(urls.onboarding); + assert.dom('[name="targetAddress"]').isVisible(); + assert.dom('[name="targetPort"]').isVisible(); + }); + + test('redirect user to success when fill targetAddress, targetPort and click Save', async function (assert) { + assert.expect(1); + await visit(urls.onboarding); + await fillIn('[name="targetAddress"]', '192.168.1.0'); + await fillIn('[name="targetPort"]', '22'); + await click('[type="submit"]'); + assert.strictEqual(currentURL(), urls.success); + }); + + test('redirect user to orgs screen when click do this later', async function (assert) { + assert.expect(1); + await visit(urls.onboarding); + await click('[name="cancel"]'); + assert.strictEqual(currentURL(), urls.orgs); + }); +}); diff --git a/ui/admin/tests/acceptance/onboarding/success-test.js b/ui/admin/tests/acceptance/onboarding/success-test.js index fb0460acd..bd2fc0caf 100644 --- a/ui/admin/tests/acceptance/onboarding/success-test.js +++ b/ui/admin/tests/acceptance/onboarding/success-test.js @@ -4,51 +4,45 @@ import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import { authenticateSession } from 'ember-simple-auth/test-support'; -module( - 'Acceptance | onboarding | quick-setup | create-resources | success', - function (hooks) { - setupApplicationTest(hooks); - setupMirage(hooks); +module('Acceptance | onboarding | success', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); - hooks.beforeEach(function () { - authenticateSession({}); - }); + hooks.beforeEach(function () { + authenticateSession({}); + }); - const urls = { - createResources: '/onboarding/quick-setup/create-resources', - successPath: '/onboarding/quick-setup/create-resources/success', - targetsPath: null, - }; + const urls = { + onboarding: '/onboarding', + success: '/onboarding/success', + target: null, + }; + const doneButtonSelector = '[data-test-onboarding-done-button]'; - test('check if the done button is present', async function (assert) { - assert.expect(1); - await visit(urls.successPath); - assert.ok(find('.onboarding-quick-setup-success-button')); - }); + test('check if the done button is present', async function (assert) { + assert.expect(1); + await visit(urls.success); + assert.dom(doneButtonSelector).isVisible(); + }); - test('check if the controller url is copyable', async function (assert) { - assert.expect(2); - await visit(urls.successPath); - assert.ok(find('.copyable')); - assert.strictEqual( - find('.copyable-button').textContent.trim(), - 'Copy to Clipboard' - ); - }); + test('check the controller url is copyable', async function (assert) { + assert.expect(2); + const origin = window.location.origin; + await visit(urls.success); + assert.dom('.copyable-content').isVisible(); + assert.strictEqual(find('.copyable-content').textContent.trim(), origin); + }); - test('redirect user to targets details when done is clicked', async function (assert) { - assert.expect(1); - await visit(urls.createResources); - await click('[type="checkbox"]'); - await fillIn('[name="hostAddress"]', '0.0.0.0'); - await fillIn('[name="targetPort"]', '22'); - await click('[type="submit"]'); - const projectId = this.server.db.scopes.where({ type: 'project' })[0].id; - const targetId = this.server.db.targets[0].id; - await visit(urls.successPath); - await click('.onboarding-quick-setup-success-button'); - urls.targetsPath = `/scopes/${projectId}/targets/${targetId}`; - assert.strictEqual(currentURL(), urls.targetsPath); - }); - } -); + test('fill the onboarding form and redirect user to target detail when done is clicked', async function (assert) { + assert.expect(1); + await visit(urls.onboarding); + await fillIn('[name="targetAddress"]', '0.0.0.0'); + await fillIn('[name="targetPort"]', '22'); + await click('[type="submit"]'); + await click(doneButtonSelector); + const projectId = this.server.db.scopes.where({ type: 'project' })[0].id; + const targetId = this.server.db.targets[0].id; + urls.target = `/scopes/${projectId}/targets/${targetId}`; + assert.strictEqual(currentURL(), urls.target); + }); +}); diff --git a/ui/admin/tests/unit/routes/onboarding/quick-setup/choose-path-test.js b/ui/admin/tests/unit/routes/onboarding/quick-setup/choose-path-test.js deleted file mode 100644 index 1d66e331c..000000000 --- a/ui/admin/tests/unit/routes/onboarding/quick-setup/choose-path-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | onboarding/quick-setup/choose-path', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:onboarding/quick-setup/choose-path'); - assert.ok(route); - }); -}); diff --git a/ui/admin/tests/unit/routes/onboarding/quick-setup/create-resources-test.js b/ui/admin/tests/unit/routes/onboarding/quick-setup/create-resources-test.js deleted file mode 100644 index ddec86774..000000000 --- a/ui/admin/tests/unit/routes/onboarding/quick-setup/create-resources-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module( - 'Unit | Route | onboarding/quick-setup/create-resources', - function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup( - 'route:onboarding/quick-setup/create-resources' - ); - assert.ok(route); - }); - } -); diff --git a/ui/admin/tests/unit/routes/onboarding/quick-setup/create-resources/success-test.js b/ui/admin/tests/unit/routes/onboarding/quick-setup/create-resources/success-test.js deleted file mode 100644 index 4994386a2..000000000 --- a/ui/admin/tests/unit/routes/onboarding/quick-setup/create-resources/success-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module( - 'Unit | Route | onboarding/quick-setup/create-resources/success', - function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup( - 'route:onboarding/quick-setup/create-resources/success' - ); - assert.ok(route); - }); - } -); diff --git a/ui/admin/tests/unit/routes/onboarding/success-test.js b/ui/admin/tests/unit/routes/onboarding/success-test.js new file mode 100644 index 000000000..a695e1ab7 --- /dev/null +++ b/ui/admin/tests/unit/routes/onboarding/success-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'admin/tests/helpers'; + +module('Unit | Route | onboarding/success', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:onboarding/success'); + assert.ok(route); + }); +});