Skip to content

Commit

Permalink
feat: factor out create link modal (#1566)
Browse files Browse the repository at this point in the history
* feat: factor out create link modal

* test: improve test

* refactor: fix tests
  • Loading branch information
pyphilia authored Nov 12, 2024
1 parent 1cbc296 commit cf440b5
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 347 deletions.
83 changes: 53 additions & 30 deletions cypress/e2e/item/create/createLink.cy.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
import { PackedFolderItemFactory } from '@graasp/sdk';

import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import { ITEM_FORM_CONFIRM_BUTTON_ID } from '../../../../src/config/selectors';
import {
GRAASP_LINK_ITEM,
GRAASP_LINK_ITEM_NO_PROTOCOL,
INVALID_LINK_ITEM,
LINK_ITEM_WITH_BLANK_NAME,
} from '../../../fixtures/links';
CREATE_ITEM_BUTTON_ID,
CREATE_ITEM_LINK_ID,
ITEM_FORM_CONFIRM_BUTTON_ID,
ITEM_FORM_LINK_INPUT_ID,
ITEM_FORM_NAME_INPUT_ID,
} from '../../../../src/config/selectors';
import { CREATE_ITEM_PAUSE } from '../../../support/constants';
import { createLink } from '../../../support/createUtils';

const openLinkModal = () => {
cy.get(`#${CREATE_ITEM_BUTTON_ID}`).click();
cy.get(`#${CREATE_ITEM_LINK_ID}`).click();
};

const createLink = ({ url }: { url: string }): void => {
openLinkModal();

cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).clear().type(url);
// wait for iframely to fill fields
cy.get(`[role=dialog]`).should('contain', 'Page title');
};

describe('Create Link', () => {
it('create link on Home', () => {
cy.setUpApi();
cy.visit(HOME_PATH);

// create
createLink(GRAASP_LINK_ITEM);
createLink({ url: 'https://graasp.org' });
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.wait('@postItem').then(() => {
// check item is created and displayed
Expand All @@ -33,7 +46,8 @@ describe('Create Link', () => {
cy.visit(HOME_PATH);

// create
createLink(GRAASP_LINK_ITEM_NO_PROTOCOL);
createLink({ url: 'graasp.org' });
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.wait('@postItem').then(() => {
// check item is created and displayed
Expand All @@ -44,6 +58,21 @@ describe('Create Link', () => {
});
});

it('enter valid link, then reset link', () => {
cy.setUpApi();
cy.visit(HOME_PATH);

// enter valid data
createLink({ url: 'graasp.org' });
cy.get(`#${ITEM_FORM_NAME_INPUT_ID} input`).should('not.be.empty');

// type a wrong link and cannot save
cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).clear().type('something');
cy.get(`#${ITEM_FORM_NAME_INPUT_ID} input`).should('not.be.empty');
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).should('be.disabled');
});

it('create link in item', () => {
const FOLDER = PackedFolderItemFactory();
const CHILD = PackedFolderItemFactory({ parentItem: FOLDER });
Expand All @@ -55,7 +84,8 @@ describe('Create Link', () => {
cy.visit(buildItemPath(id));

// create
createLink(GRAASP_LINK_ITEM);
createLink({ url: 'https://graasp.org' });
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.wait('@postItem').then(({ request: { url } }) => {
expect(url).to.contain(FOLDER.id);
Expand All @@ -69,17 +99,14 @@ describe('Create Link', () => {

describe('Error handling', () => {
it('cannot add an invalid link', () => {
const FOLDER = PackedFolderItemFactory();
cy.setUpApi({ items: [FOLDER] });
const { id } = FOLDER;
cy.setUpApi();
cy.visit(HOME_PATH);

// go to children item
cy.visit(buildItemPath(id));

// create
createLink(INVALID_LINK_ITEM, {
confirm: false,
});
// fill link and name
openLinkModal();
cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).type('invalid');
cy.get(`#${ITEM_FORM_NAME_INPUT_ID}`).type('name');
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).should(
'have.prop',
Expand All @@ -89,17 +116,13 @@ describe('Create Link', () => {
});

it('cannot have an empty name', () => {
const FOLDER = PackedFolderItemFactory();
cy.setUpApi({ items: [FOLDER] });
const { id } = FOLDER;

// go to children item
cy.visit(buildItemPath(id));
cy.setUpApi();
cy.visit(HOME_PATH);

// create
createLink(LINK_ITEM_WITH_BLANK_NAME, {
confirm: false,
});
// fill link and clear name
createLink({ url: 'https://graasp.org' });
cy.get(`#${ITEM_FORM_NAME_INPUT_ID}`).clear();
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();

cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).should(
'have.prop',
Expand Down
38 changes: 0 additions & 38 deletions cypress/fixtures/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,6 @@ export const GRAASP_LINK_ITEM: LinkItemType = PackedLinkItemFactory({
}),
});

export const GRAASP_LINK_ITEM_NO_PROTOCOL: LinkItemType = PackedLinkItemFactory(
{
creator: CURRENT_USER,
extra: buildLinkExtra({
url: 'graasp.eu',
html: '',
thumbnails: ['https://graasp.eu/img/epfl/logo-tile.png'],
icons: [
'https://graasp.eu/cdn/img/epfl/favicons/favicon-32x32.png?v=yyxJ380oWY',
],
}),
},
);

export const GRAASP_LINK_ITEM_IFRAME_ONLY: LinkItemType = PackedLinkItemFactory(
{
...GRAASP_LINK_ITEM,
Expand All @@ -59,27 +45,3 @@ export const YOUTUBE_LINK_ITEM: LinkItemType = PackedLinkItemFactory({
showLinkIframe: true,
},
});

export const INVALID_LINK_ITEM: LinkItemType = PackedLinkItemFactory({
creator: CURRENT_USER,
name: 'graasp youtube link',
description: 'a description for graasp youtube link',
extra: buildLinkExtra({
url: 'wrong link',
html: '',
thumbnails: [],
icons: [],
}),
});

export const LINK_ITEM_WITH_BLANK_NAME: LinkItemType = PackedLinkItemFactory({
creator: CURRENT_USER,
name: '',
description: 'a description for graasp youtube link',
extra: buildLinkExtra({
url: 'https://www.youtube.com/watch?v=FmiEgBMTPLo',
html: '',
thumbnails: [],
icons: [],
}),
});
22 changes: 1 addition & 21 deletions cypress/support/commands/item.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
DiscriminatedItem,
ItemType,
getAppExtra,
getDocumentExtra,
} from '@graasp/sdk';
import { DiscriminatedItem, getAppExtra, getDocumentExtra } from '@graasp/sdk';

import {
CUSTOM_APP_CYPRESS_ID,
Expand All @@ -13,7 +8,6 @@ import {
ITEM_FORM_APP_URL_ID,
ITEM_FORM_CONFIRM_BUTTON_ID,
ITEM_FORM_DOCUMENT_TEXT_SELECTOR,
ITEM_FORM_LINK_INPUT_ID,
ITEM_FORM_NAME_INPUT_ID,
ITEM_MEMBERSHIP_PERMISSION_SELECT_CLASS,
MY_GRAASP_ITEM_PATH,
Expand Down Expand Up @@ -122,20 +116,6 @@ Cypress.Commands.add(
},
);

Cypress.Commands.add(
'fillLinkModal',
({ extra = {} }, { confirm = true } = {}) => {
cy.get(`#${ITEM_FORM_LINK_INPUT_ID}`).type(
// first select all the text and then remove it to have a clear field, then type new text
`{selectall}{backspace}${extra?.[ItemType.LINK]?.url}`,
);

if (confirm) {
cy.get(`#${ITEM_FORM_CONFIRM_BUTTON_ID}`).click();
}
},
);

Cypress.Commands.add(
'fillDocumentModal',
({ name = '', extra }, { confirm = true } = {}) => {
Expand Down
15 changes: 0 additions & 15 deletions cypress/support/createUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
DiscriminatedItem,
DocumentItemType,
ItemType,
LinkItemType,
} from '@graasp/sdk';

import {
Expand All @@ -13,7 +12,6 @@ import {
CREATE_ITEM_DOCUMENT_ID,
CREATE_ITEM_FILE_ID,
CREATE_ITEM_H5P_ID,
CREATE_ITEM_LINK_ID,
CREATE_ITEM_ZIP_ID,
DASHBOARD_UPLOADER_ID,
H5P_DASHBOARD_UPLOADER_ID,
Expand Down Expand Up @@ -49,15 +47,6 @@ export const createFolder = (
cy.fillFolderModal(payload, options);
};

export const createLink = (
payload: LinkItemType,
options?: { confirm?: boolean },
): void => {
cy.get(`#${CREATE_ITEM_BUTTON_ID}`).click();
cy.get(`#${CREATE_ITEM_LINK_ID}`).click();
cy.fillLinkModal(payload, options);
};

export const createFile = (
payload: FileItemForTest,
options?: { confirm?: boolean },
Expand Down Expand Up @@ -91,10 +80,6 @@ export const createItem = (
cy.get(`#${CREATE_ITEM_BUTTON_ID}`).click();

switch (payload.type) {
case ItemType.LINK:
cy.get(`#${CREATE_ITEM_LINK_ID}`).click();
cy.fillLinkModal(payload, options);
break;
case ItemType.S3_FILE:
case ItemType.LOCAL_FILE: {
const { confirm = true } = options;
Expand Down
5 changes: 0 additions & 5 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
AppItemExtra,
DiscriminatedItem,
DocumentItemExtra,
ItemType,
PermissionLevel,
} from '@graasp/sdk';

Expand Down Expand Up @@ -53,10 +52,6 @@ declare global {

goToHome(): void;

fillLinkModal(
payload?: { extra?: { [ItemType.LINK]: { url?: string } } },
options?: { confirm?: boolean },
): void;
fillDocumentModal(
payload: {
name: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/item/form/ItemNameField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ export const ItemNameField = ({
const handleClearClick = () => {
reset({ name: '' });
};

return (
<TextField
variant="standard"
autoFocus={autoFocus}
id={ITEM_FORM_NAME_INPUT_ID}
label={translateBuilder(BUILDER.CREATE_NEW_ITEM_NAME_LABEL)}
required={required}
// always shrink because setting name from defined app does not shrink automatically
slotProps={{
inputLabel: { shrink: true },
Expand Down
67 changes: 36 additions & 31 deletions src/components/item/form/link/LinkDescriptionField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UseFormRegisterReturn } from 'react-hook-form';
import { useFormContext } from 'react-hook-form';

import { IconButton, TextField } from '@mui/material';

Expand All @@ -9,48 +9,53 @@ import { BUILDER } from '@/langs/constants';

type LinkDescriptionFieldProps = {
onRestore: () => void;
onClear: () => void;
showRestoreButton?: boolean;
form: UseFormRegisterReturn;
showClearButton?: boolean;
showRestoreButton: boolean;
};
const LinkDescriptionField = ({
form,
onRestore,
onClear,
showRestoreButton,
showClearButton,
}: LinkDescriptionFieldProps): JSX.Element => {
const {
register,
setValue,
getValues,
formState: { errors },
} = useFormContext<{ description: string }>();
const { t } = useBuilderTranslation();
const { description } = getValues();
return (
<TextField
label={t(BUILDER.DESCRIPTION_LABEL)}
variant="standard"
InputLabelProps={{ shrink: true }}
{...form}
InputProps={{
endAdornment: (
<>
<IconButton
onClick={onRestore}
sx={{
visibility: showRestoreButton ? 'visible' : 'hidden',
}}
>
<Undo2Icon size="20" />
</IconButton>
slotProps={{
inputLabel: { shrink: true },
input: {
endAdornment: (
<>
<IconButton
onClick={onRestore}
sx={{
visibility: showRestoreButton ? 'visible' : 'hidden',
}}
>
<Undo2Icon size="20" />
</IconButton>

<IconButton
onClick={onClear}
sx={{
visibility: showClearButton ? 'visible' : 'hidden',
}}
>
<XIcon size="20" />
</IconButton>
</>
),
<IconButton
onClick={() => setValue('description', '')}
sx={{
visibility: description ? 'visible' : 'hidden',
}}
>
<XIcon size="20" />
</IconButton>
</>
),
},
}}
error={Boolean(errors.description)}
helperText={errors.description?.message}
{...register('description')}
/>
);
};
Expand Down
Loading

0 comments on commit cf440b5

Please sign in to comment.