diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9e110e4e7d..f06d8de41ae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,7 @@ non-ascii paths while adding files from "Connected file share" (issue #4428) - Missed token with using social account authentication () - Fixed FBRS serverless function runtime error on images with alpha channel () - Attaching manifest with custom name () +- Uploading non-zip annotaion files () ### Security - TDB diff --git a/cvat-core/package.json b/cvat-core/package.json index 1b9b08d0ca1c..c7dfa05d6860 100644 --- a/cvat-core/package.json +++ b/cvat-core/package.json @@ -1,6 +1,6 @@ { "name": "cvat-core", - "version": "7.2.1", + "version": "7.2.2", "description": "Part of Computer Vision Tool which presents an interface for client-side integration", "main": "src/api.ts", "scripts": { diff --git a/cvat-core/src/annotations.ts b/cvat-core/src/annotations.ts index 33b5b0582310..de14ca99b4fc 100644 --- a/cvat-core/src/annotations.ts +++ b/cvat-core/src/annotations.ts @@ -305,11 +305,24 @@ export function importDataset( if (!(typeof convMaskToPoly === 'boolean')) { throw new ArgumentError('Option "convMaskToPoly" must be a boolean'); } - if (typeof file === 'string' && !file.toLowerCase().endsWith('.zip')) { - throw new ArgumentError('File must be file instance with ZIP extension'); + const allowedFileExtensions = [ + '.zip', '.xml', '.json', + ]; + const allowedFileExtensionsList = allowedFileExtensions.join(', '); + if (typeof file === 'string' && !(allowedFileExtensions.some((ext) => file.toLowerCase().endsWith(ext)))) { + throw new ArgumentError( + `File must be file instance with one of the following extensions: ${allowedFileExtensionsList}`, + ); } - if (file instanceof File && !(['application/zip', 'application/x-zip-compressed'].includes(file.type))) { - throw new ArgumentError('File must be file instance with ZIP extension'); + const allowedMimeTypes = [ + 'application/zip', 'application/x-zip-compressed', + 'application/xml', 'text/xml', + 'application/json', + ]; + if (file instanceof File && !(allowedMimeTypes.includes(file.type))) { + throw new ArgumentError( + `File must be file instance with one of the following extensions: ${allowedFileExtensionsList}`, + ); } if (instance instanceof Project) { diff --git a/tests/cypress/integration/actions_projects_models/case_103_project_export.js b/tests/cypress/integration/actions_projects_models/case_103_project_export.js index d29c55523bc0..bbaecb505305 100644 --- a/tests/cypress/integration/actions_projects_models/case_103_project_export.js +++ b/tests/cypress/integration/actions_projects_models/case_103_project_export.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -39,7 +40,7 @@ context('Export project dataset.', { browser: '!firefox' }, () => { function checkCounTasksInXML(projectParams, expectedCount) { cy.exportProject(projectParams); cy.waitForDownload(); - cy.unpackZipArchive(`cypress/fixtures/${projectParams.archiveCustomeName}.zip`); + cy.unpackZipArchive(`cypress/fixtures/${projectParams.archiveCustomName}.zip`); cy.readFile('cypress/fixtures/annotations.xml').should('exist').then((xml) => { const tasks = Cypress.$(Cypress.$.parseXML(xml)).find('task').find('name'); expect(tasks.length).to.be.eq(expectedCount); @@ -120,7 +121,7 @@ context('Export project dataset.', { browser: '!firefox' }, () => { as: 'exportAnnotationsRenameArchive', type: 'annotations', dumpType: 'CVAT for images', - archiveCustomeName: 'export_project_annotation', + archiveCustomName: 'export_project_annotation', }; // Check issue 3810 checkCounTasksInXML(exportAnnotationsRenameArchive, 2); diff --git a/tests/cypress/integration/actions_projects_models/case_104_project_export_3d.js b/tests/cypress/integration/actions_projects_models/case_104_project_export_3d.js index 1d41f6b3a665..c2094d2b5120 100644 --- a/tests/cypress/integration/actions_projects_models/case_104_project_export_3d.js +++ b/tests/cypress/integration/actions_projects_models/case_104_project_export_3d.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -86,7 +87,7 @@ context('Export project dataset with 3D task.', { browser: '!firefox' }, () => { as: 'exportAnnotations3dRenameArchive', type: 'annotations', dumpType: 'Datumaro 3D', - archiveCustomeName: 'export_project_3d_annotation', + archiveCustomName: 'export_project_3d_annotation', }; cy.exportProject(exportAnnotations3dRenameArchive); cy.waitForDownload(); diff --git a/tests/cypress/integration/actions_tasks/case_52_dump_upload_annotation.js b/tests/cypress/integration/actions_tasks/case_52_dump_upload_annotation.js index 356039f26d22..488d6545c034 100644 --- a/tests/cypress/integration/actions_tasks/case_52_dump_upload_annotation.js +++ b/tests/cypress/integration/actions_tasks/case_52_dump_upload_annotation.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -36,7 +37,7 @@ context('Dump/Upload annotation.', { browser: '!firefox' }, () => { const exportFormat = 'CVAT for images'; let annotationArchiveName = ''; - let annotationArchiveNameCustomeName = ''; + let annotationArchiveNameCustomName = ''; function uploadToTask(toTaskName) { cy.contains('.cvat-item-task-name', toTaskName) @@ -47,8 +48,8 @@ context('Dump/Upload annotation.', { browser: '!firefox' }, () => { cy.get('.cvat-modal-import-dataset').find('.cvat-modal-import-select').click(); cy.contains('.cvat-modal-import-dataset-option-item', exportFormat.split(' ')[0]).click(); cy.get('.cvat-modal-import-select').should('contain.text', exportFormat.split(' ')[0]); - cy.get('input[type="file"]').attachFile(annotationArchiveNameCustomeName, { subjectType: 'drag-n-drop' }); - cy.get(`[title="${annotationArchiveNameCustomeName}"]`).should('be.visible'); + cy.get('input[type="file"]').attachFile(annotationArchiveNameCustomName, { subjectType: 'drag-n-drop' }); + cy.get(`[title="${annotationArchiveNameCustomName}"]`).should('be.visible'); cy.contains('button', 'OK').click(); } @@ -72,12 +73,12 @@ context('Dump/Upload annotation.', { browser: '!firefox' }, () => { as: 'exportAnnotationsRenameArchive', type: 'annotations', format: exportFormat, - archiveCustomeName: 'task_export_annotation_custome_name', + archiveCustomName: 'task_export_annotation_custome_name', }; cy.exportJob(exportAnnotationRenameArchive); cy.getDownloadFileName().then((file) => { - annotationArchiveNameCustomeName = file; - cy.verifyDownload(annotationArchiveNameCustomeName); + annotationArchiveNameCustomName = file; + cy.verifyDownload(annotationArchiveNameCustomName); }); cy.verifyNotification(); }); diff --git a/tests/cypress/integration/actions_tasks3/case_113_use_default_project_storage_for_import_export_annotations.js b/tests/cypress/integration/actions_tasks3/case_113_use_default_project_storage_for_import_export_annotations.js index 6e54fb0315c5..96aa22a2a281 100644 --- a/tests/cypress/integration/actions_tasks3/case_113_use_default_project_storage_for_import_export_annotations.js +++ b/tests/cypress/integration/actions_tasks3/case_113_use_default_project_storage_for_import_export_annotations.js @@ -149,7 +149,7 @@ context('Tests for source and target storage.', () => { const exportParams = { type: 'annotations', format, - archiveCustomeName: 'job_annotations', + archiveCustomName: 'job_annotations', targetStorage: project.advancedConfiguration.targetStorage, }; cy.exportJob(exportParams); diff --git a/tests/cypress/integration/actions_tasks3/case_114_use_default_task_storage_for_import_export_annotations.js b/tests/cypress/integration/actions_tasks3/case_114_use_default_task_storage_for_import_export_annotations.js index 70d01434a2e0..9c2dde278c6a 100644 --- a/tests/cypress/integration/actions_tasks3/case_114_use_default_task_storage_for_import_export_annotations.js +++ b/tests/cypress/integration/actions_tasks3/case_114_use_default_task_storage_for_import_export_annotations.js @@ -151,7 +151,7 @@ context('Tests for source and target storage.', () => { const exportParams = { type: 'annotations', format, - archiveCustomeName: 'job_annotations', + archiveCustomName: 'job_annotations', targetStorage: project.advancedConfiguration.targetStorage, }; cy.exportJob(exportParams); diff --git a/tests/cypress/integration/actions_tasks3/case_115_use_custom_storage_for_import_export_annotations.js b/tests/cypress/integration/actions_tasks3/case_115_use_custom_storage_for_import_export_annotations.js index 5fb7e1d816ff..e0e8b14a5961 100644 --- a/tests/cypress/integration/actions_tasks3/case_115_use_custom_storage_for_import_export_annotations.js +++ b/tests/cypress/integration/actions_tasks3/case_115_use_custom_storage_for_import_export_annotations.js @@ -117,7 +117,7 @@ context('Import and export annotations: specify source and target storage in mod const exportParams = { type: 'annotations', format, - archiveCustomeName: 'job_annotations', + archiveCustomName: 'job_annotations', targetStorage: { location: 'Cloud storage', cloudStorageId: createdCloudStorageId, diff --git a/tests/cypress/integration/actions_tasks3/case_47_export_dataset.js b/tests/cypress/integration/actions_tasks3/case_47_export_dataset.js index 8454b903d863..7694ea60d785 100644 --- a/tests/cypress/integration/actions_tasks3/case_47_export_dataset.js +++ b/tests/cypress/integration/actions_tasks3/case_47_export_dataset.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -41,7 +42,7 @@ context('Export task dataset.', () => { as: 'exportDatasetRenameArchive', type: 'dataset', format: exportFormat, - archiveCustomeName: 'job_export_dataset_custome_name', + archiveCustomName: 'job_export_dataset_custome_name', }; cy.exportJob(exportDataset); cy.waitForDownload(); diff --git a/tests/cypress/integration/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js b/tests/cypress/integration/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js index 5e70853f518f..21313a02b231 100644 --- a/tests/cypress/integration/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js +++ b/tests/cypress/integration/canvas3d_functionality/case_91_canvas3d_functionality_dump_upload_annotation_point_cloud_format.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -13,7 +14,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Point Cloud" format', }; const dumpTypePC = 'Sly Point Cloud Format'; let annotationPCArchiveName = ''; - let annotationPCArchiveCustomeName = ''; + let annotationPCArchiveCustomName = ''; function confirmUpdate(modalWindowClassName) { cy.get(modalWindowClassName).should('be.visible').within(() => { @@ -63,12 +64,12 @@ context('Canvas 3D functionality. Dump/upload annotation. "Point Cloud" format', as: 'exportAnnotationsRenameArchive', type: 'annotations', format: dumpTypePC, - archiveCustomeName: 'job_export_3d_annotation_custome_name_pc_format', + archiveCustomName: 'job_export_3d_annotation_custome_name_pc_format', }; cy.exportJob(exportAnnotationRenameArchive); cy.getDownloadFileName().then((file) => { - annotationPCArchiveCustomeName = file; - cy.verifyDownload(annotationPCArchiveCustomeName); + annotationPCArchiveCustomName = file; + cy.verifyDownload(annotationPCArchiveCustomName); }); cy.verifyNotification(); cy.removeAnnotations(); diff --git a/tests/cypress/integration/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js b/tests/cypress/integration/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js index 0d6780dccc95..feef8da2dbe7 100644 --- a/tests/cypress/integration/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js +++ b/tests/cypress/integration/canvas3d_functionality/case_92_canvas3d_functionality_dump_upload_annotation_velodyne_points_format.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -13,7 +14,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Velodyne Points" form }; const dumpTypeVC = 'Kitti Raw Format'; let annotationVCArchiveName = ''; - let annotationVCArchiveNameCustomeName = ''; + let annotationVCArchiveNameCustomName = ''; function confirmUpdate(modalWindowClassName) { cy.get(modalWindowClassName).should('be.visible').within(() => { @@ -63,12 +64,12 @@ context('Canvas 3D functionality. Dump/upload annotation. "Velodyne Points" form as: 'exportAnnotationsRenameArchive', type: 'annotations', format: dumpTypeVC, - archiveCustomeName: 'job_export_3d_annotation_custome_name_vc_format', + archiveCustomName: 'job_export_3d_annotation_custome_name_vc_format', }; cy.exportJob(exportAnnotationRenameArchive); cy.getDownloadFileName().then((file) => { - annotationVCArchiveNameCustomeName = file; - cy.verifyDownload(annotationVCArchiveNameCustomeName); + annotationVCArchiveNameCustomName = file; + cy.verifyDownload(annotationVCArchiveNameCustomName); }); cy.verifyNotification(); cy.removeAnnotations(); @@ -101,7 +102,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Velodyne Points" form cy.contains('Upload annotations').click(); uploadAnnotation( dumpTypeVC.split(' ')[0], - annotationVCArchiveNameCustomeName, + annotationVCArchiveNameCustomName, '.cvat-modal-content-load-task-annotation', ); cy.contains('Annotations have been loaded').should('be.visible'); diff --git a/tests/cypress/integration/canvas3d_functionality/case_93_canvas3d_functionality_export_dataset.js b/tests/cypress/integration/canvas3d_functionality/case_93_canvas3d_functionality_export_dataset.js index a70f81722dfc..21cd6199679d 100644 --- a/tests/cypress/integration/canvas3d_functionality/case_93_canvas3d_functionality_export_dataset.js +++ b/tests/cypress/integration/canvas3d_functionality/case_93_canvas3d_functionality_export_dataset.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -50,7 +51,7 @@ context('Canvas 3D functionality. Export as a dataset.', () => { as: 'exportDatasetVCFormatRenameArchive', type: 'dataset', format: dumpTypeVC, - archiveCustomeName: 'job_export_3d_dataset_custome_name_vc_format', + archiveCustomName: 'job_export_3d_dataset_custome_name_vc_format', }; cy.exportJob(exportDatasetVCFormatRenameArchive); cy.waitForDownload(); diff --git a/tests/cypress/integration/issues_prs2/issue_5274_upload_annotations_different_file_formats.js b/tests/cypress/integration/issues_prs2/issue_5274_upload_annotations_different_file_formats.js new file mode 100644 index 000000000000..8243e5f55ee8 --- /dev/null +++ b/tests/cypress/integration/issues_prs2/issue_5274_upload_annotations_different_file_formats.js @@ -0,0 +1,67 @@ +// Copyright (C) 2022 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName, labelName } from '../../support/const'; + +context('Upload annotations in different file formats', () => { + const issueId = '5274'; + const createRectangleTrack2Points = { + points: 'By 2 Points', + type: 'Track', + labelName, + firstX: 250, + firstY: 350, + secondX: 350, + secondY: 450, + }; + + const archives = [ + { + type: 'annotations', + format: 'CVAT for images', + archiveCustomName: 'issue5274-cvat-for-images', + annotationsPath: 'annotations.xml', + }, + { + type: 'annotations', + format: 'COCO', + archiveCustomName: 'issue5274-coco', + annotationsPath: 'annotations/instances_default.json', + }, + ]; + + before(() => { + cy.openTaskJob(taskName); + cy.createRectangle(createRectangleTrack2Points); + cy.saveJob('PATCH', 200, 'saveJobDump'); + for (const archive of archives) { + cy.exportJob(archive); + cy.waitForDownload(); + cy.unpackZipArchive(`cypress/fixtures/${archive.archiveCustomName}.zip`, archive.archiveCustomName); + } + }); + + describe(`Testing issue "${issueId}"`, () => { + it('Dump annotations. Upload different file formats.', () => { + cy.removeAnnotations(); + cy.intercept('GET', '/api/jobs/**/annotations?**').as('uploadAnnotationsGet'); + for (const archive of archives) { + cy.interactMenu('Upload annotations'); + cy.uploadAnnotations( + archive.format.split(' ')[0], + `${archive.archiveCustomName}/${archive.annotationsPath}`, + '.cvat-modal-content-load-job-annotation', + ); + + cy.get('.cvat-notification-notice-upload-annotations-fail').should('not.exist'); + cy.get('#cvat_canvas_shape_1').should('exist'); + cy.get('#cvat-objects-sidebar-state-item-1').should('exist'); + + cy.removeAnnotations(); + } + }); + }); +}); diff --git a/tests/cypress/plugins/unpackZipArchive/addPlugin.js b/tests/cypress/plugins/unpackZipArchive/addPlugin.js index 37c4ee7307f4..b6341bbd4610 100644 --- a/tests/cypress/plugins/unpackZipArchive/addPlugin.js +++ b/tests/cypress/plugins/unpackZipArchive/addPlugin.js @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -6,9 +7,9 @@ const path = require('path'); const extract = require('extract-zip'); async function unpackZipArchive(args) { - const { arhivePath } = args; + const { arhivePath, extractPath } = args; const absolutePath = path.dirname(path.resolve(arhivePath)); - await extract(arhivePath, { dir: absolutePath }); + await extract(arhivePath, { dir: extractPath ? `${absolutePath}/${extractPath}/` : absolutePath }); return null; } diff --git a/tests/cypress/plugins/unpackZipArchive/unpackZipArchiveCommand.js b/tests/cypress/plugins/unpackZipArchive/unpackZipArchiveCommand.js index a96ab1a74710..cb57a24ee822 100644 --- a/tests/cypress/plugins/unpackZipArchive/unpackZipArchiveCommand.js +++ b/tests/cypress/plugins/unpackZipArchive/unpackZipArchiveCommand.js @@ -1,7 +1,9 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT -Cypress.Commands.add('unpackZipArchive', (arhivePath) => cy.task('unpackZipArchive', { +Cypress.Commands.add('unpackZipArchive', (arhivePath, extractPath) => cy.task('unpackZipArchive', { arhivePath, + extractPath, })); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 047f5af34b28..47b8f2c5cd99 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -739,7 +739,7 @@ Cypress.Commands.add('confirmUpdate', (modalWindowClassName) => { Cypress.Commands.add( 'uploadAnnotations', ( format, - file, + filePath, confirmModalClassName, sourceStorage = null, useDefaultLocation = true, @@ -766,10 +766,10 @@ Cypress.Commands.add( if (sourceStorage && sourceStorage.cloudStorageId) { cy.get('.cvat-modal-import-dataset') .find('.cvat-modal-import-filename-input') - .type(file); + .type(filePath); } else { - cy.get('input[type="file"]').attachFile(file, { subjectType: 'drag-n-drop' }); - cy.get(`[title="${file}"]`).should('be.visible'); + cy.get('input[type="file"]').attachFile(filePath, { subjectType: 'drag-n-drop' }); + cy.get(`[title="${filePath.split('/').pop()}"]`).should('be.visible'); } cy.contains('button', 'OK').click(); cy.confirmUpdate(confirmModalClassName); @@ -991,7 +991,7 @@ Cypress.Commands.add('closeModalUnsupportedPlatform', () => { }); Cypress.Commands.add('exportTask', ({ - type, format, archiveCustomeName, + type, format, archiveCustomName, }) => { cy.interactMenu('Export task dataset'); cy.get('.cvat-modal-export-task').should('be.visible').find('.cvat-modal-export-select').click(); @@ -1000,8 +1000,8 @@ Cypress.Commands.add('exportTask', ({ if (type === 'dataset') { cy.get('.cvat-modal-export-task').find('.cvat-modal-export-save-images').should('not.be.checked').click(); } - if (archiveCustomeName) { - cy.get('.cvat-modal-export-task').find('.cvat-modal-export-filename-input').type(archiveCustomeName); + if (archiveCustomName) { + cy.get('.cvat-modal-export-task').find('.cvat-modal-export-filename-input').type(archiveCustomName); } cy.contains('button', 'OK').click(); cy.get('.cvat-notification-notice-export-task-start').should('be.visible'); @@ -1009,7 +1009,7 @@ Cypress.Commands.add('exportTask', ({ }); Cypress.Commands.add('exportJob', ({ - type, format, archiveCustomeName, + type, format, archiveCustomName, targetStorage = null, useDefaultLocation = true, }) => { cy.interactMenu('Export job dataset'); @@ -1019,8 +1019,8 @@ Cypress.Commands.add('exportJob', ({ if (type === 'dataset') { cy.get('.cvat-modal-export-job').find('.cvat-modal-export-save-images').should('not.be.checked').click(); } - if (archiveCustomeName) { - cy.get('.cvat-modal-export-job').find('.cvat-modal-export-filename-input').type(archiveCustomeName); + if (archiveCustomName) { + cy.get('.cvat-modal-export-job').find('.cvat-modal-export-filename-input').type(archiveCustomName); } if (!useDefaultLocation) { cy.get('.cvat-modal-export-job').find('.cvat-settings-switch').click(); diff --git a/tests/cypress/support/commands_projects.js b/tests/cypress/support/commands_projects.js index b40b7fbccfbe..1bbc7644221e 100644 --- a/tests/cypress/support/commands_projects.js +++ b/tests/cypress/support/commands_projects.js @@ -104,7 +104,7 @@ Cypress.Commands.add('deleteProject', (projectName, projectID, expectedResult = }); Cypress.Commands.add('exportProject', ({ - projectName, type, dumpType, archiveCustomeName, + projectName, type, dumpType, archiveCustomName, }) => { cy.projectActions(projectName); cy.get('.cvat-project-actions-menu').contains('Export dataset').click(); @@ -114,8 +114,8 @@ Cypress.Commands.add('exportProject', ({ if (type === 'dataset') { cy.get('.cvat-modal-export-project').find('.cvat-modal-export-save-images').should('not.be.checked').click(); } - if (archiveCustomeName) { - cy.get('.cvat-modal-export-project').find('.cvat-modal-export-filename-input').type(archiveCustomeName); + if (archiveCustomName) { + cy.get('.cvat-modal-export-project').find('.cvat-modal-export-filename-input').type(archiveCustomName); } cy.get('.cvat-modal-export-project').contains('button', 'OK').click(); cy.get('.cvat-notification-notice-export-project-start').should('be.visible');