Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MS] Added back buttons after server choice on organization creation #8342

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion client/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1211,12 +1211,13 @@
},
"clientArea": {
"app": {
"titleLinkOrganization": "Link you customer account to your new organization",
"titleLinkOrganization": "Link your customer account to your new organization",
"titleLogin": "Log in to your customer account",
"emailLabel": "Email",
"password": "Password",
"forgottenPassword": "Forgot your password?",
"login": "Log in",
"backButton": "Back",
"saveLogin": "Remember me",
"loginFailed": "Cannot log in. Please check your email and password.",
"networkFailed": "Could not reach the server. Make sure that you are online and try again.",
Expand Down
1 change: 1 addition & 0 deletions client/src/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,7 @@
"password": "Mot de passe",
"forgottenPassword": "Mot de passe oublié ?",
"login": "Se connecter",
"backButton": "Retour",
"saveLogin": "Se souvenir de moi",
"loginFailed": "Impossible de se connecter. Veuillez vérifier votre email et mot de passe.",
"networkFailed": "Impossible de contacter le serveur. Veuillez vérifier votre connexion à internet.",
Expand Down
22 changes: 20 additions & 2 deletions client/src/views/client-area/BmsLogin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,18 @@
</ion-text>
</div>

<!-- login button -->
<!-- back and login buttons -->
<div class="saas-login-button">
<ion-text
v-show="showBackButton"
:disabled="querying"
class="button-medium custom-button custom-button-ghost saas-login-button__item"
button
@click="$emit('goBackRequested')"
>
<ion-icon :icon="arrowBack" />
{{ $msTranslate('clientArea.app.backButton') }}
</ion-text>
<ion-button
v-if="!loading"
:disabled="!emailInputRef || emailInputRef.validity !== Validity.Valid || !password.length || querying"
Expand Down Expand Up @@ -182,7 +192,7 @@
import { IonButton, IonText, IonButtons, IonFooter, IonIcon, IonSkeletonText } from '@ionic/vue';
import { MsInput, MsPasswordInput, Translatable, Validity, MsSpinner, MsCheckbox } from 'megashark-lib';
import { emailValidator } from '@/common/validators';
import { warning, arrowForward, close } from 'ionicons/icons';
import { warning, arrowBack, arrowForward, close } from 'ionicons/icons';
import { onMounted, ref } from 'vue';
import { AuthenticationToken, BmsAccessInstance, PersonalInformationResultData } from '@/services/bms';
import CreateOrganizationModalHeader from '@/components/organizations/CreateOrganizationModalHeader.vue';
Expand All @@ -191,11 +201,13 @@ import { Env } from '@/services/environment';
const props = defineProps<{
email?: string;
hideHeader?: boolean;
showBackButton?: boolean;
}>();

const emits = defineEmits<{
(e: 'loginSuccess', token: AuthenticationToken, personalInformation: PersonalInformationResultData): void;
(e: 'closeRequested'): void;
(e: 'goBackRequested'): void;
(e: 'forgottenPasswordClicked'): void;
}>();

Expand Down Expand Up @@ -343,12 +355,18 @@ async function onLoginClicked(): Promise<void> {
display: flex;
gap: 1rem;
align-items: center;
justify-content: end;
margin-top: 0.5rem;
width: 100%;

&__item {
height: 2.5rem;
border-radius: var(--parsec-radius-6);
border: 1px solid transparent;

&:hover {
border: 1px solid var(--parsec-color-light-secondary-contrast);
}
}

.skeleton-login-button {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
:disable-server-addr-field="bootstrapLink !== undefined"
:disable-organization-name-field="bootstrapLink !== undefined"
@organization-name-and-server-chosen="onOrganizationNameAndServerChosen"
@go-back-requested="$emit('backRequested')"
@close-requested="$emit('closeRequested')"
:hide-previous="true"
:hide-previous="bootstrapLink !== undefined"
/>
<organization-user-information-page
v-show="step === Steps.PersonalInformation"
Expand Down Expand Up @@ -107,6 +108,7 @@ const props = defineProps<{

const emits = defineEmits<{
(e: 'closeRequested'): void;
(e: 'backRequested'): void;
(e: 'organizationCreated', organizationName: OrganizationID, device: AvailableDevice, saveStrategy: DeviceSaveStrategy): void;
}>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@
:information-manager="informationManager"
@close-requested="onCloseRequested"
@organization-created="onOrganizationCreated"
@back-requested="onBackToServerChoice"
/>
<create-organization-custom-server
v-if="serverType === ServerType.Custom"
:bootstrap-link="bootstrapLink"
@close-requested="onCloseRequested"
@organization-created="onOrganizationCreated"
@back-requested="onBackToServerChoice"
/>
<create-organization-trial
v-if="serverType === ServerType.Trial"
:bootstrap-link="bootstrapLink"
@close-requested="onCloseRequested"
@organization-created="onOrganizationCreated"
@back-requested="onBackToServerChoice"
:information-manager="informationManager"
/>
</div>
Expand Down Expand Up @@ -103,6 +106,10 @@ async function onOrganizationCreated(
: AccessStrategy.usePassword(device, (saveStrategy as DeviceSaveStrategyPassword).password);
await modalController.dismiss({ device: device, access: accessStrategy }, MsModalResult.Confirm);
}

async function onBackToServerChoice(): Promise<void> {
serverType.value = undefined;
}
</script>

<style lang="scss" scoped>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
v-show="step === Steps.BmsLogin"
@login-success="onLoginSuccess"
@close-requested="$emit('closeRequested', false)"
@go-back-requested="$emit('backRequested')"
@forgotten-password-clicked="step = Steps.BmsForgotPassword"
:show-back-button="true"
/>
<bms-forgot-password
v-if="step === Steps.BmsForgotPassword"
Expand All @@ -20,6 +22,8 @@
:organization-name="organizationName ?? ''"
@organization-name-chosen="onOrganizationNameChosen"
:error="currentError"
@go-back-requested="$emit('backRequested')"
:hide-previous="bootstrapLink !== undefined"
@close-requested="$emit('closeRequested', false)"
/>
<organization-authentication-page
Expand Down Expand Up @@ -107,6 +111,7 @@ const props = defineProps<{

const emits = defineEmits<{
(e: 'closeRequested', force: boolean): void;
(e: 'backRequested'): void;
(e: 'organizationCreated', organizationName: OrganizationID, device: AvailableDevice, saveStrategy: DeviceSaveStrategy): void;
}>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
v-show="step === Steps.PersonalInformation"
:class="step === Steps.PersonalInformation ? 'active' : ''"
@user-information-filled="onUserInformationFilled"
@go-back-requested="$emit('backRequested')"
@close-requested="$emit('closeRequested')"
:hide-previous="true"
:hide-previous="bootstrapLink !== undefined"
/>
<organization-authentication-page
v-show="step === Steps.Authentication"
Expand Down Expand Up @@ -71,6 +72,7 @@ const props = defineProps<{

const emits = defineEmits<{
(e: 'closeRequested'): void;
(e: 'backRequested'): void;
(e: 'organizationCreated', organizationName: OrganizationID, device: AvailableDevice, saveStrategy: DeviceSaveStrategy): void;
}>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@
slot="primary"
class="modal-footer-buttons"
>
<ion-button
fill="clear"
size="default"
id="previous-button"
@click="$emit('goBackRequested')"
v-show="!hidePrevious"
>
{{ $msTranslate('CreateOrganization.button.previous') }}
<ion-icon
slot="start"
:icon="chevronBack"
size="small"
/>
</ion-button>

<ion-button
fill="solid"
size="default"
Expand All @@ -65,7 +80,7 @@
import { OrganizationID } from '@/parsec';
import { IonPage, IonButton, IonText, IonButtons, IonFooter, IonIcon } from '@ionic/vue';
import { onMounted, ref } from 'vue';
import { chevronForward } from 'ionicons/icons';
import { chevronBack, chevronForward } from 'ionicons/icons';
import { organizationValidator } from '@/common/validators';
import { Translatable, Validity, MsInput } from 'megashark-lib';
import CreateOrganizationModalHeader from '@/components/organizations/CreateOrganizationModalHeader.vue';
Expand All @@ -75,10 +90,12 @@ const props = defineProps<{
organizationName?: OrganizationID;
error?: Translatable;
disableOrganizationNameField?: boolean;
hidePrevious?: boolean;
}>();

defineEmits<{
(e: 'organizationNameChosen', name: OrganizationID): void;
(e: 'goBackRequested'): void;
(e: 'closeRequested'): void;
}>();

Expand Down
6 changes: 3 additions & 3 deletions client/tests/pw/e2e/client_area_login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ msTest('Log into the customer area', async ({ home }) => {
await expect(home.locator('.saas-login-container').locator('.saas-login__title')).toHaveText('Log in to your customer account');
await fillIonInput(home.locator('.input-container').nth(0).locator('ion-input'), DEFAULT_USER_INFORMATION.email);
await fillIonInput(home.locator('.input-container').nth(1).locator('ion-input'), DEFAULT_USER_INFORMATION.password);
await home.locator('.saas-login-button__item').click();
await home.locator('.saas-login-button__item').nth(1).click();
await expect(home.locator('.header-content').locator('.header-title')).toHaveText('Dashboard');
const logOutButton = home.locator('.header-content').locator('.custom-button').first();
await expect(logOutButton).toHaveText('Log out');
Expand All @@ -45,7 +45,7 @@ msTest('Log into the customer area failed', async ({ home }) => {
await expect(home).toHaveURL(/.+\/home$/);
await fillIonInput(home.locator('.input-container').nth(0).locator('ion-input'), DEFAULT_USER_INFORMATION.email);
await fillIonInput(home.locator('.input-container').nth(1).locator('ion-input'), 'invalid_password');
await home.locator('.saas-login-button__item').click();
await home.locator('.saas-login-button__item').nth(1).click();
const error = home.locator('.saas-login-container').locator('.login-button-error');
await expect(error).toBeVisible();
await expect(error).toHaveText('Cannot log in. Please check your email and password.');
Expand Down Expand Up @@ -140,7 +140,7 @@ for (const frozen of [false, true]) {
await button.click();
await fillIonInput(home.locator('.input-container').nth(0).locator('ion-input'), DEFAULT_USER_INFORMATION.email);
await fillIonInput(home.locator('.input-container').nth(1).locator('ion-input'), DEFAULT_USER_INFORMATION.password);
await home.locator('.saas-login-button__item').click();
await home.locator('.saas-login-button__item').nth(1).click();
const orgSwitchButton = home.locator('.sidebar-header').locator('.card-header-title');
await expect(orgSwitchButton).toHaveText('All organizations');
await orgSwitchButton.click();
Expand Down
49 changes: 49 additions & 0 deletions client/tests/pw/e2e/create_organization.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

import { expect } from '@tests/pw/helpers/assertions';
import { MockBms } from '@tests/pw/helpers/bms';
import { DEFAULT_USER_INFORMATION } from '@tests/pw/helpers/data';
import { msTest } from '@tests/pw/helpers/fixtures';
import { fillIonInput } from '@tests/pw/helpers/utils';

msTest('Opens the create organization modal', async ({ home }) => {
await expect(home.locator('#create-organization-button')).toHaveText('Create or join');
Expand Down Expand Up @@ -35,3 +38,49 @@ msTest('Opens the create organization modal', async ({ home }) => {
await modal.locator('.closeBtn').click();
await expect(modal).toBeHidden();
});

msTest('Return to server selection after selecting server type', async ({ home }) => {
await home.locator('#create-organization-button').click();
await home.locator('.popover-viewport').getByRole('listitem').nth(0).click();
const modal = home.locator('.create-organization-modal');
await expect(modal.locator('.server-modal')).toBeVisible();

// Go to saas and back
await modal.locator('.server-choice-item').nth(0).click();
await modal.locator('.server-modal-footer').locator('ion-button').nth(1).click();
await expect(modal.locator('.saas-login')).toBeVisible();
await modal.locator('.saas-login-button__item').nth(0).click();
await expect(modal.locator('.server-modal')).toBeVisible();

// Go to saas, login, and back
await modal.locator('.server-choice-item').nth(0).click();
await modal.locator('.server-modal-footer').locator('ion-button').nth(1).click();

await MockBms.mockLogin(home);
await MockBms.mockUserRoute(home);
const bmsContainer = modal.locator('.saas-login');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').nth(1);

await fillIonInput(bmsContainer.locator('ion-input').nth(0), DEFAULT_USER_INFORMATION.email);
await expect(bmsNext).toHaveDisabledAttribute();
await fillIonInput(bmsContainer.locator('ion-input').nth(1), DEFAULT_USER_INFORMATION.password);
await expect(bmsNext).not.toHaveDisabledAttribute();
await bmsNext.click();
await expect(bmsContainer).toBeHidden();
await expect(modal.locator('.organization-name-page')).toBeVisible();
await modal.locator('.organization-name-page').locator('#previous-button').click();
await expect(modal.locator('.server-modal')).toBeVisible();

// To to trial and back
await modal.locator('.server-choice-item').nth(1).click();
await modal.locator('.server-modal-footer').locator('ion-button').nth(1).click();
await expect(modal.locator('.user-information-page')).toBeVisible();
await modal.locator('.user-information-page').locator('#previous-button').click();
await expect(modal.locator('.server-modal')).toBeVisible();

// Go to custom and back
await modal.locator('.server-modal-footer').locator('ion-button').nth(0).click();
await expect(modal.locator('.organization-name-and-server-page')).toBeVisible();
await modal.locator('.organization-name-and-server-page').locator('#previous-button').click();
await expect(modal.locator('.server-modal')).toBeVisible();
});
2 changes: 1 addition & 1 deletion client/tests/pw/e2e/create_organization_custom.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ msTest('Go through custom org creation process', async ({ home }) => {
await expect(orgServerContainer.locator('.modal-header-title__text')).toHaveText('Create organization on my Parsec server');
const orgPrevious = orgServerContainer.locator('.organization-name-and-server-page-footer').locator('ion-button').nth(0);
const orgNext = orgServerContainer.locator('.organization-name-and-server-page-footer').locator('ion-button').nth(1);
await expect(orgPrevious).toBeHidden();
await expect(orgPrevious).toBeVisible();
await expect(orgNext).toHaveDisabledAttribute();
await fillIonInput(orgServerContainer.locator('ion-input').nth(0), DEFAULT_ORGANIZATION_INFORMATION.name);
await expect(orgNext).toHaveDisabledAttribute();
Expand Down
20 changes: 10 additions & 10 deletions client/tests/pw/e2e/create_organization_saas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ msTest('Go through saas org creation process', async ({ home }) => {
await MockBms.mockCreateOrganization(home, BOOTSTRAP_ADDR);

const bmsContainer = modal.locator('.saas-login');
await expect(bmsContainer.locator('.modal-header-title__text')).toHaveText('Link you customer account to your new organization');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item');
await expect(bmsContainer.locator('.modal-header-title__text')).toHaveText('Link your customer account to your new organization');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').nth(1);
await expect(bmsNext).toHaveDisabledAttribute();
await fillIonInput(bmsContainer.locator('ion-input').nth(0), DEFAULT_USER_INFORMATION.email);
await expect(bmsNext).toHaveDisabledAttribute();
Expand All @@ -55,7 +55,7 @@ msTest('Go through saas org creation process', async ({ home }) => {
await expect(bmsContainer).toBeHidden();
await expect(orgNameContainer).toBeVisible();
await expect(orgNameContainer.locator('.modal-header-title__text')).toHaveText('Create an organization');
const orgNameNext = modal.locator('.organization-name-page-footer').locator('ion-button');
const orgNameNext = modal.locator('.organization-name-page-footer').locator('ion-button').nth(1);
await expect(orgNameNext).toHaveDisabledAttribute();

await cancelAndResume(home, orgNameContainer);
Expand Down Expand Up @@ -174,8 +174,8 @@ msTest('Go through saas org creation process from bootstrap link', async ({ home
await MockBms.mockCreateOrganization(home, BOOTSTRAP_ADDR);

const bmsContainer = modal.locator('.saas-login');
await expect(bmsContainer.locator('.modal-header-title__text')).toHaveText('Link you customer account to your new organization');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item');
await expect(bmsContainer.locator('.modal-header-title__text')).toHaveText('Link your customer account to your new organization');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').nth(1);
await expect(bmsNext).toHaveDisabledAttribute();
await fillIonInput(bmsContainer.locator('ion-input').nth(0), DEFAULT_USER_INFORMATION.email);
await expect(bmsNext).toHaveDisabledAttribute();
Expand Down Expand Up @@ -262,7 +262,7 @@ msTest('Fail to login to BMS', async ({ home }) => {
await MockBms.mockLogin(home, { POST: { errors: { status: 401 } } });

const bmsContainer = modal.locator('.saas-login');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').nth(1);
await fillIonInput(bmsContainer.locator('ion-input').nth(0), DEFAULT_USER_INFORMATION.email);
await fillIonInput(bmsContainer.locator('ion-input').nth(1), DEFAULT_USER_INFORMATION.password);
await bmsNext.click();
Expand All @@ -276,7 +276,7 @@ msTest('Cannot reach the BMS', async ({ home }) => {
const modal = await openCreateOrganizationModal(home);

const bmsContainer = modal.locator('.saas-login');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').nth(1);
await fillIonInput(bmsContainer.locator('ion-input').nth(0), DEFAULT_USER_INFORMATION.email);
await fillIonInput(bmsContainer.locator('ion-input').nth(1), DEFAULT_USER_INFORMATION.password);
await bmsNext.click();
Expand All @@ -295,13 +295,13 @@ msTest('Edit from summary', async ({ home }) => {
await MockBms.mockCreateOrganization(home, BOOTSTRAP_ADDR);

const bmsContainer = modal.locator('.saas-login');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item');
const bmsNext = bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').nth(1);
await fillIonInput(bmsContainer.locator('ion-input').nth(0), DEFAULT_USER_INFORMATION.email);
await fillIonInput(bmsContainer.locator('ion-input').nth(1), DEFAULT_USER_INFORMATION.password);
await bmsNext.click();

const orgNameContainer = modal.locator('.organization-name-page');
const orgNameNext = modal.locator('.organization-name-page-footer').locator('ion-button');
const orgNameNext = modal.locator('.organization-name-page-footer').locator('ion-button').nth(1);
await fillIonInput(orgNameContainer.locator('ion-input'), DEFAULT_ORGANIZATION_INFORMATION.name);
await orgNameNext.click();

Expand Down Expand Up @@ -361,7 +361,7 @@ msTest('Try to create an org with custom order', async ({ home }) => {
const bmsContainer = modal.locator('.saas-login');
await fillIonInput(bmsContainer.locator('ion-input').nth(0), DEFAULT_USER_INFORMATION.email);
await fillIonInput(bmsContainer.locator('ion-input').nth(1), DEFAULT_USER_INFORMATION.password);
await bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').click();
await bmsContainer.locator('.saas-login-button').locator('.saas-login-button__item').nth(1).click();
await expect(modal).toBeHidden();
await expect(home).toShowInformationModal(
'Clients with a custom contract are not allowed to directly create organizations. If you need another organization, please contact us.',
Expand Down
Loading