Skip to content

Commit

Permalink
Merge pull request #10817 from linode/staging
Browse files Browse the repository at this point in the history
Release v1.126.1 - `staging` → `master`
  • Loading branch information
jaalah-akamai authored Aug 22, 2024
2 parents 8762a3c + 4726459 commit d9a6838
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 39 deletions.
6 changes: 6 additions & 0 deletions packages/manager/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## [2024-08-22] - v1.126.1

### Fix:

- Re-enable CORS for Legacy/Gen1 Endpoints ([#10812](https://github.com/linode/manager/pull/10812))

## [2024-08-19] - v1.126.0

### Added:
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "linode-manager",
"author": "Linode",
"description": "The Linode Manager website",
"version": "1.126.0",
"version": "1.126.1",
"private": true,
"type": "module",
"bugs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,96 @@
import { act, fireEvent, screen, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import userEvent from '@testing-library/user-event';
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';

import { AccessSelect, Props } from './AccessSelect';
import { AccessSelect } from './AccessSelect';

import type { Props } from './AccessSelect';
import type { ObjectStorageEndpointTypes } from '@linode/api-v4';

const CORS_ENABLED_TEXT = 'CORS Enabled';
const AUTHENTICATED_READ_TEXT = 'Authenticated Read';

vi.mock('src/components/EnhancedSelect/Select');

const mockGetAccess = vi.fn();
const mockUpdateAccess = vi.fn();
const mockGetAccess = vi.fn().mockResolvedValue({
acl: 'private',
cors_enabled: true,
});
const mockUpdateAccess = vi.fn().mockResolvedValue({});

const props: Props = {
getAccess: mockGetAccess.mockResolvedValue({ acl: 'private' }),
const defaultProps: Props = {
endpointType: 'E1',
getAccess: mockGetAccess,
name: 'my-object-name',
updateAccess: mockUpdateAccess.mockResolvedValue({}),
variant: 'object',
updateAccess: mockUpdateAccess,
variant: 'bucket',
};

describe('AccessSelect', () => {
it('shows the access', async () => {
renderWithTheme(<AccessSelect {...props} />);
const aclSelect = screen.getByRole('combobox');
const renderComponent = (props: Partial<Props> = {}) =>
renderWithTheme(<AccessSelect {...defaultProps} {...props} />);

// Confirm that combobox input field value is 'Private'.
await waitFor(() => {
expect(aclSelect).toBeEnabled();
expect(aclSelect).toHaveValue('Private');
});

// Confirm that 'Private' is selected upon opening the Autocomplete drop-down.
act(() => {
fireEvent.click(aclSelect);
fireEvent.change(aclSelect, { target: { value: 'P' } });
});

expect(screen.getByText('Private').closest('li')!).toHaveAttribute(
'aria-selected',
'true'
);
beforeEach(() => {
vi.clearAllMocks();
});

it('updates the access and submits the appropriate value', async () => {
renderWithTheme(<AccessSelect {...props} />);
it.each([
['bucket', 'E0', true],
['bucket', 'E1', true],
['bucket', 'E2', false],
['bucket', 'E3', false],
['object', 'E0', true],
['object', 'E1', true],
['object', 'E2', false],
['object', 'E3', false],
])(
'shows correct UI for %s variant and %s endpoint type',
async (variant, endpointType, shouldShowCORS) => {
renderComponent({
endpointType: endpointType as ObjectStorageEndpointTypes,
variant: variant as 'bucket' | 'object',
});

const aclSelect = screen.getByRole('combobox');

await waitFor(() => {
expect(aclSelect).toBeEnabled();
expect(aclSelect).toHaveValue('Private');
});

act(() => {
fireEvent.click(aclSelect);
fireEvent.change(aclSelect, { target: { value: 'P' } });
});

expect(screen.getByText('Private').closest('li')).toHaveAttribute(
'aria-selected',
'true'
);

if (shouldShowCORS) {
await waitFor(() => {
expect(screen.getByLabelText(CORS_ENABLED_TEXT)).toBeInTheDocument();
});
await waitFor(() => {
expect(
screen.getByRole('checkbox', { name: CORS_ENABLED_TEXT })
).toBeChecked();
});
} else {
await waitFor(() => {
expect(
screen.queryByLabelText(CORS_ENABLED_TEXT)
).not.toBeInTheDocument();
});
}
}
);

it('updates the access and CORS settings and submits the appropriate values', async () => {
renderComponent();

const aclSelect = screen.getByRole('combobox');
const saveButton = screen.getByText('Save').closest('button')!;
Expand All @@ -52,20 +100,43 @@ describe('AccessSelect', () => {
expect(aclSelect).toHaveValue('Private');
});

// Wait for CORS toggle to appear and be checked
const corsToggle = await screen.findByRole('checkbox', {
name: CORS_ENABLED_TEXT,
});
expect(corsToggle).toBeChecked();

act(() => {
// Open the dropdown
fireEvent.click(aclSelect);
fireEvent.change(aclSelect, { target: { value: 'Authenticated Read' } });

// Type to filter options
fireEvent.change(aclSelect, {
target: { value: AUTHENTICATED_READ_TEXT },
});
});

expect(aclSelect).toHaveValue('Authenticated Read');
userEvent.click(screen.getByText('Authenticated Read'));
// Wait for and select the "Authenticated Read" option
const authenticatedReadOption = await screen.findByText(
AUTHENTICATED_READ_TEXT
);
await userEvent.click(authenticatedReadOption);

await userEvent.click(corsToggle);

await waitFor(() => {
expect(screen.getByRole('combobox')).toHaveValue('Authenticated Read');
expect(aclSelect).toHaveValue(AUTHENTICATED_READ_TEXT);
expect(corsToggle).not.toBeChecked();
expect(saveButton).toBeEnabled();
});

fireEvent.click(saveButton);
await userEvent.click(saveButton);
expect(mockUpdateAccess).toHaveBeenCalledWith('authenticated-read', false);

await userEvent.click(corsToggle);
await waitFor(() => expect(corsToggle).toBeChecked());

await userEvent.click(saveButton);
expect(mockUpdateAccess).toHaveBeenCalledWith('authenticated-read', true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,10 @@ export const AccessSelect = React.memo((props: Props) => {
? 'CORS Enabled'
: 'CORS Disabled';

const isCorsEnabled =
variant === 'bucket' && endpointType !== 'E2' && endpointType !== 'E3';
const isCorsAvailable =
(variant === 'bucket' || variant === 'object') &&
endpointType !== 'E2' &&
endpointType !== 'E3';

const selectedOption =
_options.find((thisOption) => thisOption.value === selectedACL) ??
Expand Down Expand Up @@ -185,7 +187,7 @@ export const AccessSelect = React.memo((props: Props) => {
) : null}
</div>

{isCorsEnabled ? (
{isCorsAvailable ? (
<FormControlLabel
control={
<Toggle
Expand All @@ -199,7 +201,7 @@ export const AccessSelect = React.memo((props: Props) => {
/>
) : null}

{isCorsEnabled ? (
{isCorsAvailable ? (
<Typography>
Whether Cross-Origin Resource Sharing is enabled for all origins. For
more fine-grained control of CORS, please use another{' '}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export const ObjectDetailsDrawer = React.memo(
updateAccess={(acl: ACLType) =>
updateObjectACL(clusterId, bucketName, name, acl)
}
endpointType={endpointType}
name={name}
variant="object"
/>
Expand Down
5 changes: 5 additions & 0 deletions packages/manager/src/mocks/serverHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ import type {
CreateObjectStorageKeyPayload,
FirewallStatus,
NotificationType,
ObjectStorageEndpointTypes,
SecurityQuestionsPayload,
TokenRequest,
UpdateImageRegionsPayload,
Expand Down Expand Up @@ -970,9 +971,13 @@ export const handlers = [
const pageSize = Number(url.searchParams.get('page_size') || 25);

const randomBucketNumber = getRandomWholeNumber(1, 500);
const randomEndpointType = `E${Math.floor(
Math.random() * 4
)}` as ObjectStorageEndpointTypes;

const buckets = objectStorageBucketFactoryGen2.buildList(1, {
cluster: `${region}-1`,
endpoint_type: randomEndpointType,
hostname: `obj-bucket-${randomBucketNumber}.${region}.linodeobjects.com`,
label: `obj-bucket-${randomBucketNumber}`,
region,
Expand Down

0 comments on commit d9a6838

Please sign in to comment.