diff --git a/app/adapters/application.js b/app/adapters/application.js
index 8e43c7b4..8cf7756c 100644
--- a/app/adapters/application.js
+++ b/app/adapters/application.js
@@ -1,8 +1,13 @@
import JSONAPIAdapter from '@ember-data/adapter/json-api';
import { APPS } from 'website-www/constants/urls';
+
export default class ApplicationAdapter extends JSONAPIAdapter {
host = APPS.API_BACKEND;
+ headers = {
+ 'Content-Type': 'application/json',
+ };
+
ajaxOptions() {
const options = super.ajaxOptions(...arguments);
options.credentials = 'include';
diff --git a/app/adapters/user.js b/app/adapters/user.js
index 82830544..48be8bdc 100644
--- a/app/adapters/user.js
+++ b/app/adapters/user.js
@@ -7,4 +7,15 @@ export default class UserAdapter extends ApplicationAdapter {
}
return super.urlForQuery(...arguments);
}
+
+ urlForQueryRecord(query) {
+ if (query.firstname && query.lastname) {
+ return `${super.urlForQueryRecord(...arguments)}/username`;
+ }
+ return super.urlForQueryRecord(...arguments);
+ }
+
+ urlForUpdateRecord() {
+ return `${this.host}/users/self`;
+ }
}
diff --git a/app/components/signup-steps/step-one.hbs b/app/components/signup-steps/step-one.hbs
index 691b2644..4e1170fe 100644
--- a/app/components/signup-steps/step-one.hbs
+++ b/app/components/signup-steps/step-one.hbs
@@ -34,20 +34,6 @@
{{this.errorMessage.lastname}}
{{/if}}
-
-
\ No newline at end of file
diff --git a/app/components/signup-steps/step-one.js b/app/components/signup-steps/step-one.js
index 32d04f2c..a072cc48 100644
--- a/app/components/signup-steps/step-one.js
+++ b/app/components/signup-steps/step-one.js
@@ -9,6 +9,7 @@ import { APPS } from '../../constants/urls';
import { toastNotificationTimeoutOptions } from '../../constants/toast-notification';
export default class SignupStepsStepOneComponent extends Component {
@service toast;
+ @service onboarding;
@tracked data = { firstname: '', lastname: '', username: '', role: '' };
@tracked isSignupButtonDisabled = true;
@tracked isValid = true;
@@ -35,17 +36,11 @@ export default class SignupStepsStepOneComponent extends Component {
return (
!!this.data.firstname &&
!!this.data.lastname &&
- !!this.data.username &&
!!this.data.role &&
this.mavenRoleConfirm
);
} else {
- return (
- !!this.data.firstname &&
- !!this.data.lastname &&
- !!this.data.username &&
- !!this.data.role
- );
+ return !!this.data.firstname && !!this.data.lastname && !!this.data.role;
}
}
@@ -118,13 +113,28 @@ export default class SignupStepsStepOneComponent extends Component {
}
}
- @action handleButtonClick() {
- this.isSignupButtonDisabled = true;
- this.signup();
- localStorage.setItem('role', this.data.role);
- }
-
@action async signup() {
+ const { username } = await this.onboarding.generateUsername(
+ this.data.firstname,
+ this.data.lastname,
+ );
+
+ let dataToUpdate = {
+ username,
+ first_name: this.data.firstname,
+ last_name: this.data.lastname,
+ };
+
+ if (this.data.role !== 'Developer') {
+ dataToUpdate.roles = {
+ maven: this.data.role === 'Maven',
+ designer: this.data.role === 'Designer',
+ productmanager: this.data.role === 'Product Manager',
+ };
+ }
+
+ await this.onboarding.signup(dataToUpdate);
+ localStorage.setItem('role', this.data.role);
this.args.incrementStep();
}
}
diff --git a/app/constants/error-messages.js b/app/constants/error-messages.js
new file mode 100644
index 00000000..18adcff3
--- /dev/null
+++ b/app/constants/error-messages.js
@@ -0,0 +1,4 @@
+export const ERROR_MESSAGES = {
+ somethingWentWrong: 'Something went wrong!',
+ usernameGeneration: 'Username cannot be generated',
+};
diff --git a/app/models/user.js b/app/models/user.js
index d9d4c306..5df03bc5 100644
--- a/app/models/user.js
+++ b/app/models/user.js
@@ -4,7 +4,7 @@ export default class UserModel extends Model {
@attr first_name;
@attr last_name;
@attr username;
- @attr('string', { defaultValue: 'active' }) status;
+ @attr('string', { defaultValue: 'onboarding' }) status;
@attr roles;
@attr yoe;
@attr picture;
diff --git a/app/serializers/user.js b/app/serializers/user.js
index 4b080943..584e4241 100644
--- a/app/serializers/user.js
+++ b/app/serializers/user.js
@@ -10,6 +10,7 @@ export default class UserSerializer extends ApplicationSerializer {
};
return { error };
}
+
const data = payload.users.map((user) => {
const { id, ...other } = user;
return {
@@ -21,4 +22,33 @@ export default class UserSerializer extends ApplicationSerializer {
const links = { ...payload.links, first: null, last: null };
return { data, links };
}
+
+ normalizeResponse(store, primaryModelClass, payload, id, requestType) {
+ if (requestType === 'queryRecord' && payload.username) {
+ return {
+ data: {
+ id: payload.username,
+ type: primaryModelClass.modelName,
+ attributes: {
+ username: payload.username,
+ },
+ },
+ };
+ } else {
+ return super.normalizeResponse(
+ store,
+ primaryModelClass,
+ payload,
+ id,
+ requestType,
+ );
+ }
+ }
+
+ serialize() {
+ let json = super.serialize(...arguments);
+ // Remove 'id' as the user patch API does not accept it
+ delete json.id;
+ return json;
+ }
}
diff --git a/app/services/onboarding.js b/app/services/onboarding.js
new file mode 100644
index 00000000..7b025522
--- /dev/null
+++ b/app/services/onboarding.js
@@ -0,0 +1,60 @@
+import Service from '@ember/service';
+import { inject as service } from '@ember/service';
+import { TOAST_OPTIONS } from '../constants/toast-options';
+import { ERROR_MESSAGES } from '../constants/error-messages';
+
+export default class OnboardingService extends Service {
+ @service store;
+ @service toast;
+
+ async signup(dataToUpdate) {
+ try {
+ let user = this.store.peekRecord('user', dataToUpdate.username);
+
+ if (!user) {
+ user = this.store.createRecord('user', {});
+ }
+
+ if (dataToUpdate.roles) {
+ user.set('roles', {
+ ...user.get('roles'),
+ ...dataToUpdate.roles,
+ });
+ }
+
+ user.setProperties({
+ first_name: dataToUpdate.first_name,
+ last_name: dataToUpdate.last_name,
+ });
+
+ await user.save();
+ } catch (error) {
+ this.toast.error(
+ ERROR_MESSAGES.somethingWentWrong,
+ 'error!',
+ TOAST_OPTIONS,
+ );
+ }
+ }
+
+ async generateUsername(firstname, lastname) {
+ try {
+ const sanitizedFirstname = firstname.toLowerCase();
+ const sanitizedLastname = lastname.toLowerCase();
+ const user = await this.store.queryRecord('user', {
+ firstname: sanitizedFirstname,
+ lastname: sanitizedLastname,
+ dev: true,
+ });
+ if (user && user.username) {
+ return user;
+ }
+ } catch (err) {
+ this.toast.error(
+ ERROR_MESSAGES.usernameGeneration,
+ 'error!',
+ TOAST_OPTIONS,
+ );
+ }
+ }
+}
diff --git a/tests/integration/components/signup-steps/step-one-test.js b/tests/integration/components/signup-steps/step-one-test.js
index 40a47574..e88f0cff 100644
--- a/tests/integration/components/signup-steps/step-one-test.js
+++ b/tests/integration/components/signup-steps/step-one-test.js
@@ -1,4 +1,4 @@
-import { module, test, skip } from 'qunit';
+import { module, test } from 'qunit';
import { setupRenderingTest } from 'website-www/tests/helpers';
import { render, typeIn, select, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
@@ -47,67 +47,6 @@ module('Integration | Component | signup-steps/step-one', function (hooks) {
assert.dom('[data-test-input-field]').hasProperty('value', 'shubham');
});
- test('it render disable username input field and disable Generate Username button on signupDetails page', async function (assert) {
- assert.expect(19);
-
- await render(hbs``);
-
- assert
- .dom('[data-test-input-field=username]')
- .hasAttribute('name', 'username');
- assert.dom('[data-test-input=username]').hasClass('input-box');
- assert.dom('[data-test-input=username]').hasClass('input-box--btn');
-
- assert.dom('[data-test-label=username]').hasClass('label');
- assert.dom('[data-test-label=username]').hasText('Username');
- assert.dom('[data-test-label=username]').hasAttribute('for', 'username');
-
- assert.dom('[data-test-required=username]').hasClass('required');
-
- assert.dom('[data-test-input-field=username]').hasClass('input__field');
- assert.dom('[data-test-input-field=username]').hasClass('input-disable');
-
- assert.dom('[data-test-input-field=username]').hasAttribute('required');
- assert
- .dom('[data-test-input-field=username]')
- .hasAttribute('name', 'username');
- assert.dom('[data-test-input-field=username]').hasProperty('type', 'text');
- assert
- .dom('[data-test-input-field=username]')
- .hasAttribute('id', 'username');
- assert
- .dom('[data-test-input-field=username]')
- .hasProperty('disabled', true);
-
- assert.dom('[data-test-button=generateUsername]').exists();
- assert
- .dom('[data-test-button=generateUsername]')
- .hasClass('btn-generateUsername');
- assert
- .dom('[data-test-button=generateUsername]')
- .hasProperty('type', 'button');
- assert
- .dom('[data-test-button=generateUsername]')
- .hasText('Generate Username');
- assert
- .dom('[data-test-button=generateUsername]')
- .hasProperty('disabled', true);
- });
-
- test('generateUsername button is enabled when firstname and lastname input fields are not empty and valid input', async function (assert) {
- assert.expect(1);
- this.set('onInput', (e) => {
- this.value = e.target.value;
- });
-
- await render(hbs``);
- await typeIn('[data-test-input-field=firstname]', 'shubham');
- await typeIn('[data-test-input-field=lastname]', 'sigdar');
- assert
- .dom('[data-test-button=generateUsername]')
- .hasProperty('disabled', false);
- });
-
test('render select your role dropdown on signup details page ', async function (assert) {
assert.expect(11);
@@ -140,9 +79,7 @@ module('Integration | Component | signup-steps/step-one', function (hooks) {
await typeIn('[data-test-input-field=firstname]', 'shubham_1');
await typeIn('[data-test-input-field=lastname]', 'sigdar@');
assert.dom('.error__message').exists();
- assert
- .dom('[data-test-button=generateUsername]')
- .hasProperty('disabled', true);
+ assert.dom('[data-test-button=signup]').hasProperty('disabled', true);
});
test('it renders label and input checkbox when Maven role is chosen', async function (assert) {
@@ -180,17 +117,16 @@ module('Integration | Component | signup-steps/step-one', function (hooks) {
assert.dom('[data-test-button=signup]').hasText('Signup');
});
- skip('role based button should be enabled when all required fields are filled', async function (assert) {
+ test('signup button should be enabled when all required fields are filled', async function (assert) {
assert.expect(1);
await render(hbs``);
await typeIn('[data-test-input-field=firstname]', 'shubham');
await typeIn('[data-test-input-field=lastname]', 'sigdar');
- await click('[data-test-button=generateUsername]');
- select('[data-test-dropdown-field]', 'Maven');
- await click('[data-test-dropdown-option="Maven"]');
+ select('[data-test-dropdown-field]', 'Designer');
+ await click('[data-test-dropdown-option="Designer"]');
assert.dom('[data-test-button=signup]').hasProperty('disabled', false);
});
diff --git a/tests/unit/services/onboarding-test.js b/tests/unit/services/onboarding-test.js
new file mode 100644
index 00000000..5656b3c5
--- /dev/null
+++ b/tests/unit/services/onboarding-test.js
@@ -0,0 +1,106 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module('Unit | Service | onboarding', function (hooks) {
+ setupTest(hooks);
+
+ test('generateUsername method', async function (assert) {
+ assert.expect(3);
+
+ let service = this.owner.lookup('service:onboarding');
+ let store = this.owner.lookup('service:store');
+
+ store.queryRecord = () =>
+ Promise.resolve({
+ username: 'test-user',
+ get(key) {
+ return this[key];
+ },
+ });
+
+ assert.step('store.queryRecord called');
+
+ const user = await service.generateUsername('Test', 'User');
+ const username = user?.get('username');
+
+ assert.strictEqual(username, 'test-user', 'Username is correct');
+
+ assert.verifySteps(['store.queryRecord called']);
+ });
+
+ test('signup method for Developer role', async function (assert) {
+ assert.expect(4);
+
+ let service = this.owner.lookup('service:onboarding');
+ let store = this.owner.lookup('service:store');
+
+ store.peekRecord = () => null;
+
+ store.createRecord = (modelName, properties) => {
+ assert.step('store.createRecord called');
+ let mockRecord = {
+ setProperties() {
+ assert.step('setProperties called');
+ },
+ save() {
+ assert.step('save called');
+ return Promise.resolve();
+ },
+ };
+
+ Object.assign(mockRecord, properties);
+
+ return mockRecord;
+ };
+
+ let dataToUpdate = {
+ username: 'testuser',
+ };
+
+ await service.signup(dataToUpdate);
+
+ assert.verifySteps([
+ 'store.createRecord called',
+ 'setProperties called',
+ 'save called',
+ ]);
+ });
+
+ test('signup method for non-Developer role', async function (assert) {
+ assert.expect(2);
+
+ let service = this.owner.lookup('service:onboarding');
+ let store = this.owner.lookup('service:store');
+
+ store.peekRecord = () => null;
+
+ store.createRecord = (properties) => {
+ assert.step('store.createRecord called');
+
+ let mockRecord = {
+ setProperties() {
+ assert.step('setProperties called');
+ },
+ save() {
+ assert.step('save called');
+ return Promise.resolve();
+ },
+ };
+
+ Object.assign(mockRecord, properties);
+
+ return mockRecord;
+ };
+
+ let dataToUpdate = {
+ username: 'testuser',
+ roles: {
+ maven: true,
+ },
+ };
+
+ await service.signup(dataToUpdate);
+
+ assert.verifySteps(['store.createRecord called']);
+ });
+});