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

fix(Response Tabs): Tabs with a menu inside are not accessible - Response Panes #7477

Merged
merged 9 commits into from
Jun 5, 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
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ test.describe('Environment Editor', async () => {
await page.getByLabel('Request Collection').getByTestId('New Request').press('Enter');

// Add number variable to request body
await page.getByRole('tab', { name: 'Plain' }).click();
await page.getByRole('tab', { name: 'Body' }).click();
await page.locator('pre').filter({ hasText: '_.exampleObject.anotherNumber' }).press('Enter');

await page.getByTestId('CodeEditor').getByRole('textbox').press('Enter');
Expand Down
14 changes: 7 additions & 7 deletions packages/insomnia-smoke-test/tests/smoke/graphql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ test('can render schema and send GraphQL requests', async ({ app, page }) => {

// Open the graphql request
await page.getByLabel('Request Collection').getByTestId('GraphQL request').press('Enter');
await page.getByRole('tab', { name: 'GraphQL' }).click();
await page.getByRole('tab', { name: 'Body' }).click();
// Assert the schema is fetched after switching to GraphQL request
await expect(page.locator('.graphql-editor__meta')).toContainText('schema fetched just now');
await expect(page.getByText('Schema fetched just now')).toBeVisible();

// Assert schema documentation stuff
await page.getByRole('button', { name: 'schema' }).click();
Expand Down Expand Up @@ -63,9 +63,9 @@ test('can render schema and send GraphQL requests with object variables', async

// Open the graphql request
await page.getByLabel('Request Collection').getByTestId('GraphQL request with variables').press('Enter');
await page.getByRole('tab', { name: 'GraphQL' }).click();
await page.getByRole('tab', { name: 'Body' }).click();
// Assert the schema is fetched after switching to GraphQL request
await expect(page.locator('.graphql-editor__meta')).toContainText('schema fetched just now');
await expect(page.getByText('Schema fetched just now')).toBeVisible();

// Assert schema documentation stuff
await page.getByRole('button', { name: 'schema' }).click();
Expand Down Expand Up @@ -105,9 +105,9 @@ test('can render numeric environment', async ({ app, page }) => {

// Open the graphql request
await page.getByLabel('Request Collection').getByTestId('GraphQL request with number').press('Enter');
await page.getByRole('tab', { name: 'GraphQL' }).click();
await page.getByRole('tab', { name: 'Body' }).click();
// Assert the schema is fetched after switching to GraphQL request
await expect(page.locator('.graphql-editor__meta')).toContainText('schema fetched just now');
await expect(page.getByText('Schema fetched just now')).toBeVisible();

// Assert schema documentation stuff
await page.getByRole('button', { name: 'schema' }).click();
Expand Down Expand Up @@ -144,7 +144,7 @@ test('can send GraphQL requests after editing and prettifying query', async ({ a
await page.getByLabel('Request Collection').getByTestId('GraphQL request').press('Enter');

// Edit and prettify query
await page.getByRole('tab', { name: 'GraphQL' }).click();
await page.getByRole('tab', { name: 'Body' }).click();
await page.locator('pre[role="presentation"]:has-text("bearer")').click();
await page.locator('.app').press('Enter');
await page.locator('text=Prettify GraphQL').click();
Expand Down
3 changes: 2 additions & 1 deletion packages/insomnia-smoke-test/tests/smoke/oauth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ test('can make oauth2 requests', async ({ app, page }) => {
await expect(responseBody).toContainText('"sub": "admin"');

// Navigate to the OAuth2 Tab and refresh the token from there
await page.getByRole('tab', { name: 'OAuth 2' }).click();
await page.getByRole('tab', { name: 'Auth' }).click();
await expect(page.getByRole('button', { name: 'OAuth 2.0' })).toBeVisible();

const tokenInput = page.locator('[for="Access-Token"] > input');
const prevToken = await tokenInput.inputValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,13 @@ test.describe('pre-request features tests', async () => {
// set request body
await page.getByRole('tab', { name: 'Body' }).click();
await page.getByRole('button', { name: 'Body' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
await page.getByRole('option', { name: 'JSON' }).click();

const bodyEditor = page.getByTestId('CodeEditor').getByRole('textbox');
await bodyEditor.fill('{ "rawBody": {{ _.rawBody }}, "urlencodedBody": {{ _.urlencodedBody }}, "gqlBody": {{ _.gqlBody }}, "fileBody": {{ _.fileBody }}, "formdataBody": {{ _.formdataBody }} }');

// enter script
await page.getByTestId('pre-request-script-tab').click();
await page.getByRole('tab', { name: 'Scripts' }).click();
const preRequestScriptEditor = page.getByTestId('CodeEditor').getByRole('textbox');
await preRequestScriptEditor.fill(`
const rawReq = {
Expand Down Expand Up @@ -500,10 +500,10 @@ test.describe('unhappy paths', async () => {
// set request body
await page.getByRole('tab', { name: 'Body' }).click();
await page.getByRole('button', { name: 'Body' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
await page.getByRole('option', { name: 'JSON' }).click();

// enter script
await page.getByTestId('pre-request-script-tab').click();
await page.getByRole('tab', { name: 'Scripts' }).click();
const preRequestScriptEditor = page.getByTestId('CodeEditor').getByRole('textbox');
await preRequestScriptEditor.fill(tc.preReqScript);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ test('Request tabs', async ({ page }) => {
await page.getByRole('menuitemradio', { name: 'HTTP Request' }).press('Enter');
await page.getByRole('tab', { name: 'Body' }).click();
await page.getByRole('button', { name: 'Body' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
await page.getByRole('option', { name: 'JSON' }).click();
await page.getByRole('tab', { name: 'Auth' }).click();
await page.getByRole('button', { name: 'Auth' }).click();
await page.getByRole('menuitem', { name: 'OAuth 1.0' }).click();
await page.getByRole('tab', { name: 'Parameters' }).click();
await page.getByLabel('OAuth 1.0', { exact: true }).click();
await page.getByRole('tab', { name: 'Params' }).click();
await page.getByRole('tab', { name: 'Headers' }).click();
await page.getByRole('tab', { name: 'Docs' }).click();
await page.locator('text=Add Description').click();
Expand All @@ -26,11 +26,11 @@ test('WS tabs', async ({ page }) => {

await page.getByLabel('Create in collection').click();
await page.getByRole('menuitemradio', { name: 'WebSocket Request' }).click();
await page.getByRole('tab', { name: 'JSON' }).click();
await page.getByLabel('Websocket request pane tabs').getByRole('button', { name: 'JSON' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
await page.getByRole('tab', { name: 'Body' }).click();
await page.getByRole('button', { name: 'JSON' }).click();
await page.getByRole('option', { name: 'JSON' }).click();
await page.getByRole('tab', { name: 'Auth' }).click();
await page.getByRole('tab', { name: 'Parameters' }).click();
await page.getByRole('tab', { name: 'Params' }).click();
await page.getByRole('tab', { name: 'Headers' }).click();
await page.getByRole('tab', { name: 'Docs' }).click();
await page.getByRole('button', { name: 'Add Description' }).click();
Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en-US" class="w-full h-full">
<html lang="en-US" class="w-full h-full overflow-hidden">
<head>
<meta charset="utf-8" />
<meta
Expand Down
186 changes: 136 additions & 50 deletions packages/insomnia/src/ui/components/dropdowns/auth-dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { IconName } from '@fortawesome/fontawesome-svg-core';
import React, { FC, useCallback } from 'react';
import { Button, Collection, Header, ListBox, ListBoxItem, Popover, Section, Select, SelectValue } from 'react-aria-components';
import { useParams } from 'react-router-dom';

import {
getAuthTypeName,
HAWK_ALGORITHM_SHA256,
} from '../../../common/constants';
import type { AuthTypeAPIKey, AuthTypeAwsIam, AuthTypeBasic, AuthTypeNTLM, AuthTypes, RequestAuthentication } from '../../../models/request';
import { getAuthObjectOrNull } from '../../../network/authentication';
import { SIGNATURE_METHOD_HMAC_SHA1 } from '../../../network/o-auth-1/constants';
import { GRANT_TYPE_AUTHORIZATION_CODE } from '../../../network/o-auth-2/constants';
import { useRequestGroupPatcher, useRequestPatcher } from '../../hooks/use-request';
import { Dropdown, DropdownButton, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
import { Icon } from '../icon';

function castOneAuthTypeToAnother(type: AuthTypes, oldAuth: RequestAuthentication | {}): RequestAuthentication {
switch (type) {
Expand Down Expand Up @@ -133,6 +134,7 @@ interface Props {
authTypes?: AuthTypes[];
disabled?: boolean;
}

export const AuthDropdown: FC<Props> = ({ authentication, authTypes = defaultTypes, disabled = false }) => {
const { requestId, requestGroupId } = useParams() as { organizationId: string; projectId: string; workspaceId: string; requestId?: string; requestGroupId?: string };
const patchRequest = useRequestPatcher();
Expand All @@ -147,57 +149,141 @@ export const AuthDropdown: FC<Props> = ({ authentication, authTypes = defaultTyp
requestGroupId && patchRequestGroup(requestGroupId, { authentication: newAuthentication });
}, [authentication, patchRequest, patchRequestGroup, requestGroupId, requestId]);

const isSelected = useCallback((type: AuthTypes) => {
return type === getAuthObjectOrNull(authentication)?.type;
}, [authentication]);
const selectedAuthType = getAuthObjectOrNull(authentication)?.type || 'none';

const authTypesItems: {
id: AuthTypes;
name: string;
}[] = [
{
id: 'apikey',
name: 'API Key',
},
{
id: 'basic',
name: 'Basic',
},
{
id: 'digest',
name: 'Digest',
},
{
id: 'ntlm',
name: 'NTLM',
},
{
id: 'oauth1',
name: 'OAuth 1.0',
},
{
id: 'oauth2',
name: 'OAuth 2.0',
},
{
id: 'iam',
name: 'AWS IAM',
},
{
id: 'bearer',
name: 'Bearer Token',
},
{
id: 'hawk',
name: 'Hawk',
},
{
id: 'asap',
name: 'Atlassian ASAP',
},
{
id: 'netrc',
name: 'Netrc',
},
];

const authTypeSections: {
id: string;
icon: IconName;
name: string;
items: {
id: AuthTypes;
name: string;
}[];
}[] = [
{
id: 'Auth Types',
name: 'Auth Types',
icon: 'lock',
items: authTypesItems.filter(item => authTypes.includes(item.id)),
},
{
id: 'Other',
name: 'Other',
icon: 'ellipsis-h',
items: [
{
id: 'none',
name: 'None',
},
],
},
];

return (
<Dropdown
aria-label='Authentication Dropdown'
<Select
isDisabled={disabled}
triggerButton={
<DropdownButton className="tall !text-[--hl]">
{getAuthTypeName(getAuthObjectOrNull(authentication)?.type)}
<i className="fa fa-caret-down space-left" />
</DropdownButton>
}
aria-label="Change Authentication type"
name="auth-type"
onSelectionChange={authType => {
onClick(authType as AuthTypes);
}}
selectedKey={selectedAuthType}
>
<DropdownSection
aria-label='Auth types section'
title="Auth Types"
>
{authTypes.map(authType =>
<DropdownItem
key={authType}
aria-label={getAuthTypeName(authType, true)}
>
<ItemContent
icon={isSelected(authType) ? 'check' : 'empty'}
label={getAuthTypeName(authType, true)}
onClick={() => onClick(authType)}
/>
</DropdownItem>
)}
</DropdownSection>
<DropdownSection
aria-label="Other types section"
title="Other"
>
<DropdownItem aria-label='None' key="none">
<ItemContent
icon={isSelected('none') ? 'check' : 'empty'}
label={'No Authentication'}
onClick={() => onClick('none')}
/>
</DropdownItem>
<DropdownItem aria-label='Inherit from parent' key="inherit">
<ItemContent
icon={getAuthObjectOrNull(authentication) === null ? 'check' : 'empty'}
label={'Inherit from parent'}
onClick={() => onClick()}
/>
</DropdownItem>
</DropdownSection>
</Dropdown>
<Button className="px-4 min-w-[17ch] py-1 font-bold flex flex-1 items-center justify-between gap-2 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm">
<SelectValue className="flex truncate items-center justify-center gap-2">
{({ selectedText }) => (
<div className='flex items-center gap-2 text-[--hl]'>
{selectedText || 'Auth Type'}
</div>
)}
</SelectValue>
<Icon icon="caret-down" />
</Button>
<Popover className="min-w-max">
<ListBox
items={authTypeSections}
className="border select-none text-sm min-w-max border-solid border-[--hl-sm] shadow-lg bg-[--color-bg] py-2 rounded-md overflow-y-auto max-h-[85vh] focus:outline-none"
>
{item => (
<Section>
<Header className='pl-2 py-1 flex items-center gap-2 text-[--hl] text-xs uppercase'>
<Icon icon={item.icon} /> <span>{item.name}</span>
</Header>
<Collection items={item.items}>
{item => (
<ListBoxItem
className="flex gap-2 px-[--padding-md] aria-selected:font-bold items-center text-[--color-font] h-[--line-height-xs] w-full text-md whitespace-nowrap bg-transparent hover:bg-[--hl-sm] disabled:cursor-not-allowed focus:bg-[--hl-xs] focus:outline-none transition-colors"
aria-label={item.name}
textValue={item.name}
>
{({ isSelected }) => (
<>
<span>{item.name}</span>
{isSelected && (
<Icon
icon="check"
className="text-[--color-success] justify-self-end"
/>
)}
</>
)}
</ListBoxItem>
)}
</Collection>
</Section>
)}
</ListBox>
</Popover>
</Select>
);
};
Loading
Loading