Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import { createBucket } from '@linode/api-v4/lib/object-storage';
import { getNewRegionLabel } from '@linode/utilities';
import { authenticate } from 'support/api/authentication';
import {
interceptGetNetworkUtilization,
Expand All @@ -16,6 +17,7 @@
interceptGetBuckets,
interceptUpdateBucketAccess,
} from 'support/intercepts/object-storage';
import { interceptGetRegions } from 'support/intercepts/regions';
import { ui } from 'support/ui';
import { cleanUp } from 'support/util/cleanup';
import { chooseCluster } from 'support/util/clusters';
Expand All @@ -27,6 +29,8 @@
createObjectStorageBucketFactoryLegacy,
} from 'src/factories';

import type { Region } from '@linode/api-v4/lib/object-storage';

/**
* Create a bucket with the given label and cluster.
*
Expand All @@ -41,23 +45,29 @@
*/
const setUpBucket = (
label: string,
cluster: string,
region: string,
cors_enabled: boolean = true
) => {
return createBucket(
createObjectStorageBucketFactoryLegacy.build({
cluster,
region,
cors_enabled,
label,

// API accepts either `cluster` or `region`, but not both. Our factory
// populates both fields, so we have to manually set `region` to `undefined`
// populates both fields, so we have to manually set `cluster` to `undefined`
// to avoid 400 responses from the API.
region: undefined,
cluster: undefined,
})
);
};

const setupBuckets = (bucketsDetails: { label: string; region: string }[]) => {
return Promise.all(
bucketsDetails.map(({ label, region }) => setUpBucket(label, region))
);
};

authenticate();
beforeEach(() => {
cy.tag('method:e2e');
Expand All @@ -78,12 +88,11 @@
cy.tag('purpose:syntheticTesting');
const bucketLabel = randomLabel();
const bucketClusterObj = chooseCluster();
const bucketCluster = bucketClusterObj.id;
const bucketRegion = getRegionById(bucketClusterObj.region).label;
const bucketHostname = `${bucketLabel}.${bucketClusterObj.domain}`;
interceptGetBuckets().as('getBuckets');
interceptCreateBucket().as('createBucket');
interceptDeleteBucket(bucketLabel, bucketCluster).as('deleteBucket');
interceptDeleteBucket().as('deleteBucket');
interceptGetNetworkUtilization().as('getNetworkUtilization');

mockGetAccount(accountFactory.build({ capabilities: ['Object Storage'] }));
Expand Down Expand Up @@ -157,17 +166,15 @@
it('can update bucket access', () => {
const bucketLabel = randomLabel();
const bucketClusterObj = chooseCluster();
const bucketCluster = bucketClusterObj.id;
const bucketAccessPage = `/object-storage/buckets/${bucketCluster}/${bucketLabel}/access`;
const bucketRegion = bucketClusterObj.region;
const bucketAccessPage = `/object-storage/buckets/${bucketRegion}/${bucketLabel}/access`;

cy.defer(
() => setUpBucket(bucketLabel, bucketCluster),
() => setUpBucket(bucketLabel, bucketRegion),
'creating Object Storage bucket'
).then(() => {
interceptGetBucketAccess(bucketLabel, bucketCluster).as(
'getBucketAccess'
);
interceptUpdateBucketAccess(bucketLabel, bucketCluster).as(
interceptGetBucketAccess(bucketLabel, bucketRegion).as('getBucketAccess');
interceptUpdateBucketAccess(bucketLabel, bucketRegion).as(
'updateBucketAccess'
);

Expand Down Expand Up @@ -197,4 +204,155 @@
cy.findByText('Bucket access updated successfully.');
});
});

/*
* - Confirms that user can filter bucket list by region.
*/
it('can filter the list of buckets by region', () => {
interceptGetBuckets().as('getBuckets');
interceptGetRegions().as('getRegions');

const bucketsDetails = new Array(2).fill({}).map((_, index) => ({
label: randomLabel(),
region: index === 0 ? 'us-ord' : 'us-lax',
}));

cy.defer(
() => setupBuckets(bucketsDetails),
'creating Object Storage bucket'
).then(() => {
cy.visitWithLogin('/object-storage/buckets');
cy.wait(['@getBuckets', '@getRegions']).then(([_, { response }]) => {

Check warning on line 225 in packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":225,"column":31,"nodeType":"Literal","endLine":225,"endColumn":44}
const regions: Region[] = response?.body.data;

const selectedBucket = bucketsDetails[0];
const selectedRegion = regions.find(
(region) => region.id === selectedBucket.region

Check warning on line 230 in packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":230,"column":20,"nodeType":null,"endLine":230,"endColumn":22}
);

expect(
selectedRegion,
`expected region matching ${selectedBucket.region}`
).to.exist;

const selectedRegionLabel = selectedRegion
Copy link
Contributor

Choose a reason for hiding this comment

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

The test should target a specific scenario - the region should either be defined, or not. In this case, I recommend to fail early in case region is undefined:

expect(selectedRegion, `expected region matching ${selectedBucket.cluster.region}`).to.exist;

Copy link
Contributor

Choose a reason for hiding this comment

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

Since new we know that region exists, this line could be simplified to

const selectedRegionLabel = getNewRegionLabel(selectedRegion)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am afraid it couldn't, find() method always return T | undefined, the region is 100% there, it is purely a typization issue.

? getNewRegionLabel(selectedRegion)
: '';

const regionSelect = ui.autocomplete
.findByLabel('Region')
.should('be.visible')
.type(selectedRegionLabel);

ui.autocompletePopper
.findByTitle(selectedRegionLabel, { exact: false })
.should('be.visible')
.click();

regionSelect.click();

cy.get('tbody').within(() => {

Check warning on line 254 in packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":254,"column":35,"nodeType":null,"endLine":254,"endColumn":37}
cy.get('tr')
.should('have.length', 1)

Check warning on line 256 in packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":256,"column":21,"nodeType":"Literal","endLine":256,"endColumn":34}
.within(() => {
cy.findByText(selectedBucket.label).should('be.visible');
});
});
});
});
});

/*
* - Confirms that user can filter bucket list by endpoint.
*/
it('can filter the list of buckets by endpoint', () => {
interceptGetBuckets().as('getBuckets');
interceptGetRegions().as('getRegions');

const bucketsDetails = new Array(2).fill({}).map((_, index) => ({
label: randomLabel(),
region: index === 0 ? 'us-ord' : 'us-lax',
}));

cy.defer(
() => setupBuckets(bucketsDetails),
'creating Object Storage bucket'
).then(() => {
cy.visitWithLogin('/object-storage/buckets');
cy.wait(['@getBuckets', '@getRegions']);

const selectedBucket = bucketsDetails[0];
const selectedBucketRegion = selectedBucket.region;

const endpointSelect = ui.autocomplete.findByLabel('Endpoint');
endpointSelect.should('be.visible').type(selectedBucketRegion);
ui.autocompletePopper
.findByTitle(selectedBucketRegion, { exact: false })
.should('be.visible')
.click();
endpointSelect.click();

cy.get('tbody').within(() => {
cy.get('tr')
.should('have.length', 1)
.within(() => {

Check warning on line 298 in packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":298,"column":22,"nodeType":null,"endLine":298,"endColumn":24}
cy.findByText(selectedBucket.label).should('be.visible');
});
});
});
});

/*
* - Confirms that when region is selected, endpoint multiselect.
* shows only endpoints related to the selected region.
*/
it('should filter list of endpoints when region is selected', () => {
interceptGetBuckets().as('getBuckets');
interceptGetRegions().as('getRegions');

const bucketsDetails = new Array(2).fill({}).map((_, index) => ({
label: randomLabel(),
region: index === 0 ? 'us-ord' : 'us-lax',
}));

cy.defer(
() => setupBuckets(bucketsDetails),
'creating Object Storage bucket'
).then(() => {
cy.visitWithLogin('/object-storage/buckets');
cy.wait(['@getBuckets', '@getRegions']).then(([_, { response }]) => {
const regions: Region[] = response?.body.data;

const selectedBucket = bucketsDetails[0];
const selectedRegion = regions.find(
(region) => region.id === selectedBucket.region

Check warning on line 328 in packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":328,"column":20,"nodeType":null,"endLine":328,"endColumn":22}
);

expect(
selectedRegion,
`expected region matching ${selectedBucket.region}`
).to.exist;

const selectedRegionLabel = selectedRegion
? getNewRegionLabel(selectedRegion)
: '';

const regionSelect = ui.autocomplete
.findByLabel('Region')
.should('be.visible')
.type(selectedRegionLabel);
ui.autocompletePopper
.findByTitle(selectedRegionLabel, { exact: false })
.should('be.visible')
.click();
regionSelect.click();

ui.autocomplete.findByLabel('Endpoint').should('be.visible').click();

ui.autocompletePopper
.findByTitle(new RegExp('^.*-.*-.*\..*.'))

Check warning on line 353 in packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Unnecessary escape character: \.. Raw Output: {"ruleId":"no-useless-escape","severity":1,"message":"Unnecessary escape character: \..","line":353,"column":45,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":353,"endColumn":46,"suggestions":[{"messageId":"removeEscape","fix":{"range":[11376,11377],"text":""},"desc":"Remove the `\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[11376,11376],"text":"\"},"desc":"Replace the `\` with `\\` to include the actual backslash character."}]}
.should('have.length', 1);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export const interceptDeleteBucket = (
apiMatcher(`object-storage/buckets/${cluster}/*`)
);
}
return cy.intercept('DELETE', apiMatcher('object-storage/buckets/*'));
return cy.intercept('DELETE', apiMatcher('object-storage/buckets/**/*'));
};

/**
Expand Down
9 changes: 9 additions & 0 deletions packages/manager/cypress/support/intercepts/regions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ import { makeResponse } from 'support/util/response';
import type { Region, RegionAvailability } from '@linode/api-v4';
import type { ExtendedRegion } from 'support/util/regions';

/**
* Intercepts GET regions request.
*
* @returns Cypress chainable.
*/
export const interceptGetRegions = (): Cypress.Chainable<null> => {
return cy.intercept('GET', apiMatcher('regions*'));
};

/**
* Intercepts GET request to fetch Linode regions and mocks response.
*
Expand Down
Loading
Loading