diff --git a/cypress.config.js b/cypress.config.js index f6f3e83b05..a7b7561885 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -3,7 +3,7 @@ const { defineConfig } = require('cypress'); module.exports = defineConfig({ e2e: { baseUrl: 'http://localhost:3000', - supportFile: false, + supportFile: 'cypress/support/index.js', experimentalStudio: true, retries: process.env.CI ? 2 : 0, }, diff --git a/cypress/e2e/phase_1/admin_display.cy.js b/cypress/e2e/phase_1/admin_display.cy.js new file mode 100644 index 0000000000..90997f51c4 --- /dev/null +++ b/cypress/e2e/phase_1/admin_display.cy.js @@ -0,0 +1,60 @@ +import { teardown } from '../../support/authentication'; +import * as datasetImportPage from '../../support/datasetImportPage'; +import * as fields from '../../support/fields'; +import * as menu from '../../support/menu'; + +describe('Home Page', () => { + beforeEach(() => { + teardown(); + menu.openAdvancedDrawer(); + menu.goToAdminDashboard(); + + datasetImportPage.importDataset('dataset/film.csv'); + }); + + it('should automatically bind home page field icon', () => { + cy.findByRole('link', { + name: /Display/, + }).click(); + + fields.createNewField(); + + cy.findByRole('gridcell', { + name: /newField/, + timeout: 1000, + }) + .should('be.visible') + .then((parent) => { + cy.findByTestId('HomeIcon', { + container: parent, + timeout: 500, + }).should('be.visible'); + }); + }); + + it('should automatically bind main resource page field icon', () => { + cy.findByRole('link', { + name: /Display/, + timeout: 1000, + }).click(); + + cy.findByRole('menuitem', { + name: /Main resource/, + timeout: 1000, + }).click(); + + fields.createNewField(); + + cy.findByRole('gridcell', { + name: /newField/, + timeout: 1000, + }) + .should('be.visible') + .then((parent) => { + cy.findByTestId('ArticleIcon', { + container: parent, + timeout: 500, + }).should('be.visible'); + }); + }); +}); diff --git a/cypress/e2e/phase_1/subresource_ark.cy.js b/cypress/e2e/phase_1/subresource_ark.cy.js new file mode 100644 index 0000000000..f9c4f7c7c0 --- /dev/null +++ b/cypress/e2e/phase_1/subresource_ark.cy.js @@ -0,0 +1,58 @@ +import { teardown } from '../../support/authentication'; +import * as datasetImportPage from '../../support/datasetImportPage'; +import * as menu from '../../support/menu'; + +describe('Ark subresource', () => { + beforeEach(() => { + teardown(); + menu.openAdvancedDrawer(); + menu.goToAdminDashboard(); + datasetImportPage.importDataset('dataset/subresources-ark-data.json'); + datasetImportPage.importModel('model/subresources-ark-model.tar'); + }); + + it('should display a back button when going to subresource', () => { + datasetImportPage.publish(); + datasetImportPage.goToPublishedResources(); + + cy.findByRole('link', { + name: /Search/, + timeout: 500, + }) + .should('be.visible') + .click(); + + cy.findByText('Publication n°1', { + timeout: 500, + }) + .should('be.visible') + .click(); + + cy.findByText('Publication n°1', { + timeout: 500, + }) + .should('be.visible') + .click(); + + cy.findByRole('link', { + name: 'uid:/2acebfc7a1e13123df28821e41424d80', + timeout: 500, + }).click(); + + cy.findByText('Canidae', { + timeout: 500, + }).should('be.visible'); + + cy.findByRole('button', { + name: 'Go back to resource', + timeout: 500, + }) + .should('be.visible') + .click(); + + cy.findByRole('link', { + name: 'uid:/2acebfc7a1e13123df28821e41424d80', + timeout: 500, + }).should('be.visible'); + }); +}); diff --git a/cypress/fixtures/dataset/subresources-ark-data.json b/cypress/fixtures/dataset/subresources-ark-data.json new file mode 100644 index 0000000000..bca5ad73da --- /dev/null +++ b/cypress/fixtures/dataset/subresources-ark-data.json @@ -0,0 +1,26 @@ +[ + { + "uri": "ark:/67375/WNG-J6G37RM9-Q", + "name": "Publication n°1", + "most_found_animal_species": { + "id": 90, + "name": "Canidae" + } + }, + { + "uri": "ark:/67375/WNG-J6G37RM9-R", + "name": "Publication n°2", + "most_found_animal_species": { + "id": 91, + "name": "Felinae" + } + }, + { + "uri": "ark:/67375/WNG-J6G37RM9-S", + "name": "Publication n°3", + "most_found_animal_species": { + "id": 90, + "name": "Canidae" + } + } +] diff --git a/cypress/fixtures/model/subresources-ark-model.tar b/cypress/fixtures/model/subresources-ark-model.tar new file mode 100644 index 0000000000..c0b83e3e6b Binary files /dev/null and b/cypress/fixtures/model/subresources-ark-model.tar differ diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 0000000000..b634ea9b36 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1 @@ +import '@testing-library/cypress/add-commands'; diff --git a/cypress/support/fields.js b/cypress/support/fields.js index ecd9f78bd9..20f81010b6 100644 --- a/cypress/support/fields.js +++ b/cypress/support/fields.js @@ -10,3 +10,25 @@ export function setFieldLanguage(fieldName, languageCode) { cy.wait(1000); } + +export function createNewField() { + cy.findByRole('button', { + name: /New field/, + }).click(); + + cy.findByRole('textbox', { + name: /Label/, + }).should('be.visible', { + timeout: 1000, + }); + + cy.findByRole('button', { + name: /Arbitrary value/, + }).click(); + + cy.findByPlaceholderText('Enter an arbitrary value').type('FIELD'); + + cy.findByRole('button', { + name: /Save/, + }).click(); +} diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 0000000000..1221b17e09 --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1 @@ +import './commands'; diff --git a/jsconfig.json b/jsconfig.json index 9a71a76f49..34ba59ebbe 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -4,7 +4,8 @@ "module": "commonjs", "allowSyntheticDefaultImports": true, "resolveJsonModule": true, - "jsx": "react" + "jsx": "react", + "types": ["cypress", "@testing-library/cypress"] }, "exclude": ["src/build", "node_modules"] } diff --git a/package-lock.json b/package-lock.json index cbd6dce4cb..bfdad2dfbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -183,6 +183,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.23.10", + "@testing-library/cypress": "10.0.2", "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "11.2.7", "@types/ejs": "3.1.5", @@ -7386,6 +7387,172 @@ "node": ">=10" } }, + "node_modules/@testing-library/cypress": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@testing-library/cypress/-/cypress-10.0.2.tgz", + "integrity": "sha512-dKv95Bre5fDmNb9tOIuWedhGUryxGu1GWYWtXDqUsDPcr9Ekld0fiTb+pcBvSsFpYXAZSpmyEjhoXzLbhh06yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.14.6", + "@testing-library/dom": "^10.1.0" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "cypress": "^12.0.0 || ^13.0.0" + } + }, + "node_modules/@testing-library/cypress/node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/cypress/node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/cypress/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/cypress/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/cypress/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/cypress/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/cypress/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/cypress/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/cypress/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/cypress/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/cypress/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/cypress/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@testing-library/dom": { "version": "7.31.2", "dev": true, @@ -24006,9 +24173,11 @@ } }, "node_modules/lz-string": { - "version": "1.4.4", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, - "license": "WTFPL", + "license": "MIT", "bin": { "lz-string": "bin/bin.js" } diff --git a/package.json b/package.json index 299f24dda0..1da6309abe 100644 --- a/package.json +++ b/package.json @@ -252,6 +252,7 @@ "compression-webpack-plugin": "10.0.0", "copy-webpack-plugin": "11.0.0", "cypress": "13.6.4", + "@testing-library/cypress": "10.0.2", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.8", "eslint": "^8.56.0", diff --git a/src/api/services/createDiacriticSafeContainRegex.js b/src/api/services/createDiacriticSafeContainRegex.js index 0b6e456e4e..ceebf8a36e 100644 --- a/src/api/services/createDiacriticSafeContainRegex.js +++ b/src/api/services/createDiacriticSafeContainRegex.js @@ -48,7 +48,7 @@ const multipleCharsMappings = { [`${singleCharsMappings.s}${singleCharsMappings.s}`]: `((${singleCharsMappings.s}${singleCharsMappings.s})|ß)`, [`${singleCharsMappings.t}${singleCharsMappings.h}`]: `((${singleCharsMappings.t}${singleCharsMappings.h})|þ)`, [`${singleCharsMappings.t}${singleCharsMappings.s}`]: `((${singleCharsMappings.t}${singleCharsMappings.s})|ƾ|ʦ|ʧ)`, - [`${singleCharsMappings.y}${singleCharsMappings.t}`]: `((${singleCharsMappings.y}${singleCharsMappings.t})|Ʀ)`, + [`${singleCharsMappings.y}${singleCharsMappings.r}`]: `((${singleCharsMappings.y}${singleCharsMappings.r})|Ʀ)`, }; const composedCharactersMappings = { diff --git a/src/app/js/fields/FieldGrid.js b/src/app/js/fields/FieldGrid.js index c1f8919b67..5d97c7cab6 100644 --- a/src/app/js/fields/FieldGrid.js +++ b/src/app/js/fields/FieldGrid.js @@ -358,7 +358,7 @@ const DraggableItemGrid = compose( }; return ( - + ( { const matches = useRouteMatch(); @@ -16,36 +16,44 @@ export const FieldToggleInternalScopeComponent = ({ input, p: polyglot }) => { const [values, setValues] = React.useState([]); React.useEffect(() => { + if (!input) { + return; + } + setValues((currentValues) => { if (currentValues.length > 0) { return currentValues; } if (matches.params.filter === 'dataset') { + input.onChange(['home']); return ['home']; } if (matches.params.subresourceId) { + input.onChange(['subRessource']); return ['subRessource']; } if (matches.params.filter === 'document') { + input.onChange(['document']); return ['document']; } if (matches.params.filter === 'graphic') { + input.onChange(['chart']); return ['chart']; } return []; }); - }, [matches]); + }, [input, matches]); useEffect(() => { Array.isArray(input.value) && setValues(input.value); }, [input.value]); - const handleStateSelected = (event, newValues) => { + const handleStateSelected = (_, newValues) => { setValues(newValues); input.onChange(newValues); }; diff --git a/src/app/js/public/resource/Resource.js b/src/app/js/public/resource/Resource.js index 5cf037eb5c..8a0008ad3d 100644 --- a/src/app/js/public/resource/Resource.js +++ b/src/app/js/public/resource/Resource.js @@ -1,30 +1,30 @@ -import React, { useMemo } from 'react'; -import PropTypes from 'prop-types'; +import BackIcon from '@mui/icons-material/ArrowBack'; +import HomeIcon from '@mui/icons-material/Home'; +import { Button, Card, CardActions, CardContent } from '@mui/material'; import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import React from 'react'; import { connect } from 'react-redux'; -import compose from 'recompose/compose'; import { withRouter } from 'react-router-dom'; -import translate from 'redux-polyglot/translate'; -import HomeIcon from '@mui/icons-material/Home'; -import BackIcon from '@mui/icons-material/ArrowBack'; -import { CardContent, CardActions, Card, Button } from '@mui/material'; import { Swipeable } from 'react-swipeable'; +import compose from 'recompose/compose'; +import translate from 'redux-polyglot/translate'; -import { fromResource, fromSearch } from '../selectors'; -import { fromFields, fromCharacteristic } from '../../sharedSelectors'; -import Detail from './Detail'; -import RemovedDetail from './RemovedDetail'; -import { polyglot as polyglotPropTypes } from '../../propTypes'; -import Loading from '../../lib/components/Loading'; -import { preLoadResource } from './'; +import get from 'lodash/get'; +import isEqual from 'lodash/isEqual'; +import { getResourceUri } from '../../../../common/uris'; import { preLoadPublication } from '../../fields'; -import { preLoadExporters } from '../export'; import Link from '../../lib/components/Link'; -import stylesToClassname from '../../lib/stylesToClassName'; +import Loading from '../../lib/components/Loading'; import NavButton, { NEXT, PREV } from '../../lib/components/NavButton'; -import isEqual from 'lodash/isEqual'; -import get from 'lodash/get'; -import { getResourceUri } from '../../../../common/uris'; +import stylesToClassname from '../../lib/stylesToClassName'; +import { polyglot as polyglotPropTypes } from '../../propTypes'; +import { fromCharacteristic, fromFields } from '../../sharedSelectors'; +import { preLoadExporters } from '../export'; +import { fromResource, fromSearch } from '../selectors'; +import { preLoadResource } from './'; +import Detail from './Detail'; +import RemovedDetail from './RemovedDetail'; const navStyles = stylesToClassname( { @@ -96,6 +96,7 @@ export class ResourceComponent extends React.Component { ) { this.setState({ lastResourceUri: match.params.uri }); } else if (match.params && match.params.naan && match.params.rest) { + // This does not work as state is reset when going from an ARK resource to a subresource // Ark resources const lastResourceUri = this._getArkResourceUrl( match.params.naan,