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

test: [M3-7508] - Add tests to check Parent and Child Close Account flows #10296

Merged
merged 1 commit into from
Mar 26, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import {
} from 'support/util/random';
import type { CancelAccount } from '@linode/api-v4';
import { mockWebpageUrl } from 'support/intercepts/general';
import {
mockAppendFeatureFlags,
mockGetFeatureFlagClientstream,
} from 'support/intercepts/feature-flags';
import { makeFeatureFlagData } from 'support/util/feature-flags';

// Data loss warning which is displayed in the account cancellation dialog.
const cancellationDataLossWarning =
Expand All @@ -30,6 +35,18 @@ const cancellationPaymentErrorMessage =
'We were unable to charge your credit card for services rendered. \
We cannot cancel this account until the balance has been paid.';

// Tooltip message that appears when a child account tries to close the account.
const contactParentUserTooltipsMessage =
'Contact your parent user to close your account.';

// Tooltip message that appears when a child account tries to close the account.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Tooltip message that appears when a child account tries to close the account.
// Tooltip message that appears when a proxy account tries to close the account.

Let's clarify this by saying proxy.

const contactCustomerSupportTooltipsMessage =
'Contact customer support to close this account.';

// Tooltip message that appears when a parent account with one and more child accounts tries to close the account.
const removeChildAccountTooltipsMessage =
'Remove child accounts before closing the account.';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We started created constants for things like this which would be nice to use especially when verbiage changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for the other two if @jdamore-linode approves, but I understand if in a testing scenario we want to keep things silo'd

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree @jaalah-akamai, I think using the constants from src here would make sense.

(The only situations where I might insist that the strings be defined in the tests rather than imported from src is when the language itself is really important, like with notices and warnings involving billing or potential data loss, and that's specifically so the tests break if the language changes: it basically forces us to double check the wording and gives us an extra opportunity to spot typos, etc.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup I agree with that


describe('Account cancellation', () => {
/*
* - Confirms that a user can cancel their account from the Account Settings page.
Expand Down Expand Up @@ -198,3 +215,223 @@ describe('Account cancellation', () => {
});
});
});

describe('Parent/Child account cancellation', () => {
/*
* - Confirms that a child user cannot close the account.
*/
it('disables the "Close Account" button for a child user', () => {
const mockAccount = accountFactory.build({});
const mockProfile = profileFactory.build({
username: 'mock-child-user',
restricted: false,
user_type: 'child',
});

mockGetAccount(mockAccount).as('getAccount');
mockGetProfile(mockProfile).as('getProfile');

// Navigate to Account Settings page, click "Close Account" button.
cy.visitWithLogin('/account/settings');
cy.wait(['@getAccount', '@getProfile']);

ui.accordion
.findByTitle('Close Account')
.should('be.visible')
.within(() => {
ui.button
.findByTitle('Close Account')
.should('be.visible')
.should('be.disabled')
.trigger('mouseover');
// Click the button first, then confirm the tooltip is shown.
ui.tooltip
.findByText(contactParentUserTooltipsMessage)
.should('be.visible');
});
});

/**
* Confirms that a proxy account cannot close the account
*/
it('disables a proxy account to close the account', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('disables a proxy account to close the account', () => {
it('disables "Close Account" button for proxy users', () => {

const mockAccount = accountFactory.build();
const mockProfile = profileFactory.build({
username: 'proxy-user',
restricted: false,
user_type: 'proxy',
});

mockGetAccount(mockAccount).as('getAccount');
mockGetProfile(mockProfile).as('getProfile');

// Navigate to Account Settings page, click "Close Account" button.
cy.visitWithLogin('/account/settings');
cy.wait(['@getAccount', '@getProfile']);

ui.accordion
.findByTitle('Close Account')
.should('be.visible')
.within(() => {
ui.button
.findByTitle('Close Account')
.should('be.visible')
.should('be.disabled')
.trigger('mouseover');
// Click the button first, then confirm the tooltip is shown.
ui.tooltip
.findByText(contactCustomerSupportTooltipsMessage)
.should('be.visible');
});
});

/**
* Confirms that a parent account with one or more active child accounts cannot close the account
*/
it('disables a parent account with one or more active child accounts to close the account', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('disables a parent account with one or more active child accounts to close the account', () => {
it('disables "Close Account" button for parent users', () => {

const mockAccount = accountFactory.build();
const mockProfile = profileFactory.build({
username: 'parent-user',
restricted: false,
user_type: 'parent',
});

mockGetAccount(mockAccount).as('getAccount');
mockGetProfile(mockProfile).as('getProfile');

// Navigate to Account Settings page, click "Close Account" button.
cy.visitWithLogin('/account/settings');
cy.wait(['@getAccount', '@getProfile']);

ui.accordion
.findByTitle('Close Account')
.should('be.visible')
.within(() => {
ui.button
.findByTitle('Close Account')
.should('be.visible')
.should('be.disabled')
.trigger('mouseover');
// Click the button first, then confirm the tooltip is shown.
ui.tooltip
.findByText(removeChildAccountTooltipsMessage)
.should('be.visible');
});
});

/**
* Confirms that a parent account with no active child accounts can close the account
*/
it('allows a default account with no active child accounts to close the account', () => {
const mockAccount = accountFactory.build();
const mockProfile = profileFactory.build({
username: 'default-user',
restricted: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test shouldn't pass in real life because a restricted user should not be able to close an account, as we test in an earlier test case. Depending on what access they have for the account_access grant, they may not be able to see the Settings page at all (they'll get a 403 unauthorized on /account and see an error), or they may be able to see the page and click the Close Account button (with read_only or read_write access), but will see the final Close Account button within the modal greyed out because they are still a restricted user.

So, we should mock an unrestricted default account here:

Suggested change
restricted: true,
restricted: false,

If a restricted default user with read_only/read_write account_access:
Screenshot 2024-03-20 at 8 03 56 AM

If a restricted default user with null account_access:
Screenshot 2024-03-20 at 8 04 49 AM

user_type: 'default',
});
const mockCancellationResponse: CancelAccount = {
survey_link: `https://${randomDomainName()}/${randomString(5)}`,
};

const cancellationComments = randomPhrase();

// TODO: Parent/Child - M3-7559 clean up when feature is live in prod and feature flag is removed.
mockAppendFeatureFlags({
parentChildAccountAccess: makeFeatureFlagData(true),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientStream');

mockGetAccount(mockAccount).as('getAccount');
mockGetProfile(mockProfile).as('getProfile');
mockCancelAccountError(cancellationPaymentErrorMessage, 409).as(
'cancelAccount'
);
mockWebpageUrl(
mockCancellationResponse.survey_link,
'This is a mock webpage to confirm Cloud Manager survey link behavior'
).as('getSurveyPage');

// Navigate to Account Settings page, click "Close Account" button.
cy.visitWithLogin('/account/settings');
cy.wait(['@getAccount', '@getProfile']);

ui.accordion
.findByTitle('Close Account')
.should('be.visible')
.within(() => {
ui.button
.findByTitle('Close Account')
.should('be.visible')
.should('be.enabled')
.click();
});

ui.dialog
.findByTitle('Are you sure you want to close your Linode account?')
.should('be.visible')
.within(() => {
cy.findByText(cancellationDataLossWarning, { exact: false }).should(
'be.visible'
);

// Confirm that submit button is disabled before entering required info.
ui.button
.findByTitle('Close Account')
.should('be.visible')
.should('be.disabled');

// Enter username, confirm that submit button becomes enabled, and click
// the submit button.
cy.findByLabelText(
`Please enter your Username (${mockProfile.username}) to confirm.`
)
.should('be.visible')
.should('be.enabled')
.type(mockProfile.username);

ui.button
.findByTitle('Close Account')
.should('be.visible')
.should('be.enabled')
.click();

// Confirm that request payload contains expected data and API error
// message is displayed in the dialog.
cy.wait('@cancelAccount').then((intercept) => {
expect(intercept.request.body['comments']).to.equal('');
});

cy.findByText(cancellationPaymentErrorMessage).should('be.visible');

// Enter account cancellation comments, click "Close Account" again,
// and this time mock a successful account cancellation response.
mockCancelAccount(mockCancellationResponse).as('cancelAccount');
cy.contains('Comments (optional)').click().type(cancellationComments);

ui.button
.findByTitle('Close Account')
.should('be.visible')
.should('be.enabled')
.click();

cy.wait('@cancelAccount').then((intercept) => {
expect(intercept.request.body['comments']).to.equal(
cancellationComments
);
});
});

// Confirm that Cloud presents account cancellation screen and prompts the
// user to complete the exit survey. Confirm that clicking survey button
// directs the user to the expected URL.
cy.findByText('It’s been our pleasure to serve you.').should('be.visible');
ui.button
.findByTitle('Take our exit survey')
.should('be.visible')
.should('be.enabled')
.click();

cy.wait('@getSurveyPage');
cy.url().should('equal', mockCancellationResponse.survey_link);
});
});
Loading