diff --git a/site/gatsby-site/playwright/e2e-full/submit.spec.ts b/site/gatsby-site/playwright/e2e-full/submit.spec.ts index 309b4c65ab..acd3c0d899 100644 --- a/site/gatsby-site/playwright/e2e-full/submit.spec.ts +++ b/site/gatsby-site/playwright/e2e-full/submit.spec.ts @@ -1497,4 +1497,54 @@ test.describe('The Submit form', () => { await expect(page.locator('.tw-toast:has-text("Please verify all information programmatically pulled from the report")')).toBeVisible(); await expect(page.locator('.tw-toast:has-text("Error fetching news.")')).not.toBeVisible(); }); + + test('Should autocomplete new entities', async ({ page, skipOnEmptyEnvironment }) => { + + await conditionalIntercept( + page, + '**/parseNews**', + () => true, + parseNews, + 'parseNews' + ); + + await trackRequest( + page, + '**/graphql', + (req) => req.postDataJSON().operationName == 'FindSubmissions', + 'findSubmissions' + ); + + await page.goto(url); + + await waitForRequest('findSubmissions'); + + await page.locator('input[name="url"]').fill( + `https://www.arstechnica.com/gadgets/2017/11/youtube-to-crack-down-on-inappropriate-content-masked-as-kids-cartoons/` + ); + + await page.locator('button:has-text("Fetch info")').click(); + + await waitForRequest('parseNews'); + + await page.locator('[name="incident_date"]').fill('2020-01-01'); + + await expect(page.locator('.form-has-errors')).not.toBeVisible(); + + await page.locator('[data-cy="to-step-2"]').click(); + + await page.locator('[data-cy="to-step-3"]').click(); + + await page.locator('input[name="developers"]').fill('New entity'); + await page.keyboard.press('Enter'); + + await page.locator('input[name="deployers"]').fill('New entity'); + + await page.locator('#deployers-tags .dropdown-item[aria-label="New entity"]').click(); + + await page.locator('button[type="submit"]').click(); + + await expect(page.locator('.tw-toast:has-text("Report successfully added to review queue. You can see your submission")')).toBeVisible(); + await expect(page.locator(':text("Please review. Some data is missing.")')).not.toBeVisible(); + }); }); \ No newline at end of file diff --git a/site/gatsby-site/playwright/e2e-full/signup.spec.ts b/site/gatsby-site/playwright/e2e/signup.spec.ts similarity index 100% rename from site/gatsby-site/playwright/e2e-full/signup.spec.ts rename to site/gatsby-site/playwright/e2e/signup.spec.ts diff --git a/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js b/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js index 66ce40eddc..85cd649f7b 100644 --- a/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js +++ b/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js @@ -160,6 +160,8 @@ const FormDetails = ({ const [submitCount, setSubmitCount] = useState(0); + const [entityNamesList, setEntityNamesList] = useState(entityNames); + const { isRole } = useUserContext(); const { @@ -191,6 +193,26 @@ const FormDetails = ({ } }, [submissionFailed, submissionComplete, submissionReset]); + const handleEntityChange = (values) => { + // Update entityNamesList with adding values that are not in entityNamesList + const newEntityNamesList = values + .filter((value) => { + if (!value.label) { + return !entityNamesList.includes(value); + } + return !entityNamesList.includes(value?.label); + }) + .map((entity) => { + if (entity.label) { + return entity.label; + } else { + return entity; + } + }); + + setEntityNamesList([...entityNamesList, ...newEntityNamesList]); + }; + const saveInLocalStorage = useRef( debounce((values) => { localStorage.setItem('formValues', JSON.stringify(values)); @@ -265,8 +287,8 @@ const FormDetails = ({ placeholder={t('Who employed or was responsible for the technology?')} className="mt-3" schema={schema} - options={entityNames} - handleChange={handleChange} + options={entityNamesList} + handleChange={handleEntityChange} handleBlur={handleBlur} touched={touched} values={values} @@ -282,8 +304,8 @@ const FormDetails = ({ placeholder={t('Who created or built the technology involved in the incident?')} className="mt-3" schema={schema} - options={entityNames} - handleChange={handleChange} + options={entityNamesList} + handleChange={handleEntityChange} handleBlur={handleBlur} touched={touched} values={values} @@ -299,8 +321,8 @@ const FormDetails = ({ placeholder={t('Who experienced negative impacts?')} className="mt-3" schema={schema} - options={entityNames} - handleChange={handleChange} + options={entityNamesList} + handleChange={handleEntityChange} handleBlur={handleBlur} touched={touched} values={values} diff --git a/site/gatsby-site/src/components/forms/TagsControl.js b/site/gatsby-site/src/components/forms/TagsControl.js index 18893712ae..45746b4a83 100644 --- a/site/gatsby-site/src/components/forms/TagsControl.js +++ b/site/gatsby-site/src/components/forms/TagsControl.js @@ -3,7 +3,14 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import Tags from './Tags'; -const TagsControl = ({ name, placeholder, className, disabled = false, options = undefined }) => { +const TagsControl = ({ + name, + placeholder, + className, + disabled = false, + options = undefined, + handleChange = undefined, +}) => { const { 0: { value }, 2: { setTouched, setValue }, @@ -20,6 +27,9 @@ const TagsControl = ({ name, placeholder, className, disabled = false, options = onChange={(value) => { setTouched(true); setValue(value); + if (handleChange && handleChange.length > 0) { + handleChange(value); + } }} {...{ name, @@ -29,6 +39,6 @@ const TagsControl = ({ name, placeholder, className, disabled = false, options = }} /> ); -} +}; export default TagsControl; diff --git a/site/gatsby-site/src/components/forms/TagsInputGroup.js b/site/gatsby-site/src/components/forms/TagsInputGroup.js index 4b11381a6b..d6b47b6875 100644 --- a/site/gatsby-site/src/components/forms/TagsInputGroup.js +++ b/site/gatsby-site/src/components/forms/TagsInputGroup.js @@ -41,7 +41,9 @@ const TagsInputGroup = ({ } data-cy={props['data-cy']} > - +
{isInvalid ? errors[name] : null}