diff --git a/cypress/e2e/item/create/createLink.cy.ts b/cypress/e2e/item/create/createLink.cy.ts index 0d6fccfe6..4306c94dd 100644 --- a/cypress/e2e/item/create/createLink.cy.ts +++ b/cypress/e2e/item/create/createLink.cy.ts @@ -2,7 +2,11 @@ import { HOME_PATH, buildItemPath } from '../../../../src/config/paths'; import { ITEM_FORM_CONFIRM_BUTTON_ID } from '../../../../src/config/selectors'; import ITEM_LAYOUT_MODES from '../../../../src/enums/itemLayoutModes'; import { SAMPLE_ITEMS } from '../../../fixtures/items'; -import { GRAASP_LINK_ITEM, INVALID_LINK_ITEM } from '../../../fixtures/links'; +import { + GRAASP_LINK_ITEM, + GRAASP_LINK_ITEM_NO_PROTOCOL, + INVALID_LINK_ITEM, +} from '../../../fixtures/links'; import { CREATE_ITEM_PAUSE } from '../../../support/constants'; import { createLink } from '../../../support/createUtils'; @@ -25,6 +29,24 @@ describe('Create Link', () => { }); }); + it('create link without protocol on Home', () => { + cy.setUpApi(); + cy.visit(HOME_PATH); + + cy.switchMode(ITEM_LAYOUT_MODES.LIST); + + // create + createLink(GRAASP_LINK_ITEM_NO_PROTOCOL); + + cy.wait('@postItem').then(() => { + // check item is created and displayed + cy.wait(CREATE_ITEM_PAUSE); + + // expect update + cy.wait('@getOwnItems'); + }); + }); + it('create folder in item', () => { cy.setUpApi(SAMPLE_ITEMS); const { id } = SAMPLE_ITEMS.items[0]; diff --git a/cypress/fixtures/links.ts b/cypress/fixtures/links.ts index 1d8caf9b1..aea6e9663 100644 --- a/cypress/fixtures/links.ts +++ b/cypress/fixtures/links.ts @@ -10,7 +10,9 @@ export const GRAASP_LINK_ITEM: EmbeddedLinkItemType = { description: 'a description for graasp link', path: 'ecafbd2a_5688_11eb_ae93_0242ac130002', creator: CURRENT_USER, - settings: {}, createdAt: new Date(), updatedAt: new Date(), + settings: {}, + createdAt: new Date(), + updatedAt: new Date(), extra: buildEmbeddedLinkExtra({ url: 'https://graasp.eu', html: '', @@ -21,6 +23,26 @@ export const GRAASP_LINK_ITEM: EmbeddedLinkItemType = { }), }; +export const GRAASP_LINK_ITEM_NO_PROTOCOL: EmbeddedLinkItemType = { + id: 'ecafbd2a-5688-11eb-ae91-0242ac130002', + type: ItemType.LINK, + name: 'graasp link', + description: 'a description for graasp link', + path: 'ecafbd2a_5688_11eb_ae93_0242ac130002', + creator: CURRENT_USER, + settings: {}, + createdAt: new Date(), + updatedAt: new Date(), + extra: buildEmbeddedLinkExtra({ + 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: EmbeddedLinkItemType = { ...GRAASP_LINK_ITEM, id: 'ecafbd2a-5688-11eb-ae91-0242ac130122', @@ -35,7 +57,9 @@ export const YOUTUBE_LINK_ITEM: EmbeddedLinkItemType = { type: ItemType.LINK, name: 'graasp youtube link', description: 'a description for graasp youtube link', - settings: {}, createdAt: new Date(), updatedAt: new Date(), + settings: {}, + createdAt: new Date(), + updatedAt: new Date(), creator: CURRENT_USER, path: 'gcafbd2a_5688_11eb_ae93_0242ac130002', extra: buildEmbeddedLinkExtra({ @@ -51,7 +75,9 @@ export const INVALID_LINK_ITEM: EmbeddedLinkItemType = { path: 'gcafbd2a_5688_11eb_ae93_0242ac130001', type: ItemType.LINK, creator: CURRENT_USER, - settings: {}, createdAt: new Date(), updatedAt: new Date(), + settings: {}, + createdAt: new Date(), + updatedAt: new Date(), name: 'graasp youtube link', description: 'a description for graasp youtube link', extra: buildEmbeddedLinkExtra({ diff --git a/cypress/support/server.ts b/cypress/support/server.ts index c58dea383..402c0274a 100644 --- a/cypress/support/server.ts +++ b/cypress/support/server.ts @@ -1816,7 +1816,7 @@ export const mockPublishItem = (items: ItemForTest[]): void => { export const mockUnpublishItem = (items: ItemForTest[]): void => { cy.intercept( { - method: HttpMethod.POST, + method: HttpMethod.DELETE, url: new RegExp(`${API_HOST}/${buildItemUnpublishRoute(ID_FORMAT)}`), }, ({ reply, url }) => { @@ -1928,7 +1928,7 @@ export const mockUpdatePassword = ( export const mockGetItemFavorites = ( itemFavorites: ItemFavorite[], - shouldThrowError: boolean + shouldThrowError: boolean, ): void => { cy.intercept( { @@ -1964,9 +1964,7 @@ export const mockAddFavorite = ( ).as('favoriteItem'); }; -export const mockDeleteFavorite = ( - shouldThrowError: boolean, -): void => { +export const mockDeleteFavorite = (shouldThrowError: boolean): void => { cy.intercept( { method: HttpMethod.DELETE, diff --git a/package.json b/package.json index e3f2975a2..a82052503 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@emotion/react": "11.11.1", "@emotion/styled": "11.11.0", "@graasp/chatbox": "2.0.0", - "@graasp/query-client": "1.1.5", + "@graasp/query-client": "1.2.0", "@graasp/sdk": "1.1.2", "@graasp/translations": "1.15.1", "@graasp/ui": "3.2.4", @@ -78,7 +78,7 @@ "prettier:check": "prettier --check src/**/*.{js,ts,tsx,json}", "prettier:write": "prettier --write src/**/*.{js,ts,tsx,json}", "cypress:open": "env-cmd -f ./.env.local cypress open", - "cypress": "concurrently \"yarn start:test\" \"wait-on http://localhost:3111 && yarn cypress:run\"", + "cypress": "concurrently -k -s first \"yarn start:test\" \"wait-on http://localhost:3111 && yarn cypress:run\"", "cypress:run": "env-cmd -f ./.env.test cypress run --headless --browser chrome", "postinstall": "husky install" }, diff --git a/public/graasp.svg b/public/graasp.svg new file mode 100644 index 000000000..0dc7adce9 --- /dev/null +++ b/public/graasp.svg @@ -0,0 +1,50 @@ + + diff --git a/public/index.html b/public/index.html index 72089c093..dbd01842a 100644 --- a/public/index.html +++ b/public/index.html @@ -2,11 +2,11 @@
- + { const { t: translateBuilder } = useBuilderTranslation(); const handleLinkInput: TextFieldProps['onChange'] = (event) => { + const newValue = event.target.value; + const hasProtocol = /^https?:\/\//; onChange({ ...item, name: translateBuilder(BUILDER.LINK_DEFAULT_NAME), // todo: this is replaced by iframely extra: buildEmbeddedLinkExtra({ - url: event.target.value, + // when used inside the NewItem Modal this component does not receive the item prop + // so the https will not show, but it will be added when we submit the url. + url: hasProtocol.test(newValue) ? newValue : `https://${newValue}`, html: '', thumbnails: [], icons: [], diff --git a/src/utils/item.ts b/src/utils/item.ts index 15379e3de..f5f213ad0 100644 --- a/src/utils/item.ts +++ b/src/utils/item.ts @@ -86,14 +86,15 @@ export const isUrlValid = (str?: string): boolean => { return false; } const pattern = new RegExp( - '^(https?:\\/\\/)+' + // protocol + '^(https?:\\/\\/)?' + // protocol is optional '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name - '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address + '((\\d{1,3}\\.){3}\\d{1,3})|' + // OR ip (v4) address + 'localhost)' + // OR localhost alias '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string - '(\\#[-a-z\\d_]*)?$', + '(\\#\\S*)?$', // fragment locator 'i', - ); // fragment locator + ); return pattern.test(str); }; diff --git a/yarn.lock b/yarn.lock index 1921660f8..bacaffe6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4746,12 +4746,12 @@ __metadata: languageName: node linkType: hard -"@graasp/query-client@npm:1.1.5": - version: 1.1.5 - resolution: "@graasp/query-client@npm:1.1.5" +"@graasp/query-client@npm:1.2.0": + version: 1.2.0 + resolution: "@graasp/query-client@npm:1.2.0" dependencies: "@graasp/sdk": 1.1.2 - "@graasp/translations": 1.15.0 + "@graasp/translations": 1.15.1 axios: 0.27.2 crypto-js: 4.1.1 http-status-codes: 2.2.0 @@ -4761,7 +4761,7 @@ __metadata: uuid: 9.0.0 peerDependencies: react: ^17.0.0 - checksum: 6e7303043ff731eddd2d2172586c4d8bb425a14bf359285c9f80f036e320aa86777ad8129c516d76f40e760b2963d2af8add7fdba37b970b9146be0c1b1d2cdd + checksum: eaf962ccb577c0e38368fd078e4f0a4e5682a3da32df8e7edb00080eafc21902bc760bd850a46242a962154d0164ce4f2710f71c0a5278cee24383607cab6e0b languageName: node linkType: hard @@ -4784,15 +4784,6 @@ __metadata: languageName: node linkType: hard -"@graasp/translations@npm:1.15.0": - version: 1.15.0 - resolution: "@graasp/translations@npm:1.15.0" - dependencies: - i18next: 22.4.15 - checksum: e073b83374462f2d108f1d697b7aa14460d05f4ec2e10757db0e0a0a72574bff4721f997851d8e88e871f3704c8bcf887533590dd7acdcbf9d607109bae25c8a - languageName: node - linkType: hard - "@graasp/translations@npm:1.15.1": version: 1.15.1 resolution: "@graasp/translations@npm:1.15.1" @@ -12156,7 +12147,7 @@ __metadata: "@emotion/react": 11.11.1 "@emotion/styled": 11.11.0 "@graasp/chatbox": 2.0.0 - "@graasp/query-client": 1.1.5 + "@graasp/query-client": 1.2.0 "@graasp/sdk": 1.1.2 "@graasp/translations": 1.15.1 "@graasp/ui": 3.2.4