diff --git a/README.md b/README.md index 3be0907cd..c6e758b18 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,45 @@ First install node >=16 and npm >=8. Recommended versions `node v19` and `npm v9`. +### Create a `.npmrc` file and add a token + +To install the [@iqss/dataverse-client-javascript](https://github.com/IQSS/dataverse-client-javascript/pkgs/npm/dataverse-client-javascript) +from the GitHub registry, necessary for connecting with the Dataverse API, follow these steps to create an `.npmrc` file in +the root of your project using your GitHub token. + +1. **Copy `.npmrc.example`** + + Duplicate the `.npmrc.example` file in your project and save it as `.npmrc`. + +2. **Replace the Token** + + Open the newly created `.npmrc` file and replace `YOUR_GITHUB_TOKEN` with your actual GitHub token. + + ```plaintext + legacy-peer-deps=true + + //npm.pkg.github.com/:_authToken= + @iqss:registry=https://npm.pkg.github.com/ + ``` + +#### How to Get a GitHub Token + +If you don't have a GitHub token yet, follow these steps: + +1. Go to your GitHub account settings. + +2. Navigate to "Developer settings" -> "Personal access tokens." + +3. Click "Personal access tokens" -> "Tokens (classic)" -> "Generate new token (classic)". + +4. Give the token a name and select the "read:packages" scope. + +5. Copy the generated token. + +6. Replace `YOUR_GITHUB_AUTH_TOKEN` in the `.npmrc` file with the copied token. + +Now, you should be able to install the Dataverse JavaScript client using npm. + ### `npm install` Run this command to install the dependencies. You may see a message about vulnerabilities after running this command. \ diff --git a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts index 2b624ca35..02f05124b 100644 --- a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts +++ b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts @@ -15,6 +15,8 @@ import { } from '@iqss/dataverse-client-javascript' import { JSDatasetMapper } from '../mappers/JSDatasetMapper' +const includeDeaccessioned = true + export class DatasetJSDataverseRepository implements DatasetRepository { getByPersistentId( persistentId: string, @@ -22,7 +24,7 @@ export class DatasetJSDataverseRepository implements DatasetRepository { requestedVersion?: string ): Promise { return getDataset - .execute(persistentId, this.versionToVersionId(version)) + .execute(persistentId, this.versionToVersionId(version), includeDeaccessioned) .then((jsDataset) => Promise.all([ jsDataset, diff --git a/src/files/domain/models/File.ts b/src/files/domain/models/File.ts index d95434292..7ecc13ecc 100644 --- a/src/files/domain/models/File.ts +++ b/src/files/domain/models/File.ts @@ -124,7 +124,7 @@ export class FileEmbargo { export interface FileTabularData { variablesCount: number observationsCount: number - unf: string + unf?: string } export enum FileLabelType { @@ -171,12 +171,12 @@ export class File { readonly type: FileType, readonly size: FileSize, readonly date: FileDate, - public downloadCount: number, + readonly downloadCount: number, readonly labels: FileLabel[], public readonly isDeleted: boolean, public readonly ingest: FileIngest, readonly checksum?: FileChecksum, - public thumbnail?: string, + readonly thumbnail?: string, readonly directory?: string, readonly embargo?: FileEmbargo, readonly tabularData?: FileTabularData, diff --git a/src/files/domain/models/FileCriteria.ts b/src/files/domain/models/FileCriteria.ts index 8de6c9dab..30720deba 100644 --- a/src/files/domain/models/FileCriteria.ts +++ b/src/files/domain/models/FileCriteria.ts @@ -62,6 +62,15 @@ export class FileCriteria { searchText ) } + + get someFilterApplied(): boolean { + return ( + this.filterByType !== undefined || + this.filterByAccess !== undefined || + this.filterByTag !== undefined || + this.searchText !== undefined + ) + } } export enum FileSortByOption { diff --git a/src/files/infrastructure/FileJSDataverseRepository.ts b/src/files/infrastructure/FileJSDataverseRepository.ts index e16c3c004..e96c994bf 100644 --- a/src/files/infrastructure/FileJSDataverseRepository.ts +++ b/src/files/infrastructure/FileJSDataverseRepository.ts @@ -1,5 +1,5 @@ import { FileRepository } from '../domain/repositories/FileRepository' -import { File, FilePublishingStatus } from '../domain/models/File' +import { File } from '../domain/models/File' import { FilesCountInfo } from '../domain/models/FilesCountInfo' import { FilePaginationInfo } from '../domain/models/FilePaginationInfo' import { FileUserPermissions } from '../domain/models/FileUserPermissions' @@ -10,7 +10,10 @@ import { getDatasetFilesTotalDownloadSize, getFileDownloadCount, getFileUserPermissions, - ReadError + ReadError, + File as JSFile, + getFileDataTables, + FileDataTable as JSFileTabularData } from '@iqss/dataverse-client-javascript' import { FileCriteria } from '../domain/models/FileCriteria' import { DomainFileMapper } from './mappers/DomainFileMapper' @@ -30,7 +33,6 @@ export class FileJSDataverseRepository implements FileRepository { criteria: FileCriteria = new FileCriteria() ): Promise { const jsPagination = DomainFileMapper.toJSPagination(paginationInfo) - return getDatasetFiles .execute( datasetPersistentId, @@ -41,48 +43,54 @@ export class FileJSDataverseRepository implements FileRepository { DomainFileMapper.toJSFileSearchCriteria(criteria), DomainFileMapper.toJSFileOrderCriteria(criteria.sortBy) ) - .then((jsFiles) => jsFiles.map((jsFile) => JSFileMapper.toFile(jsFile, datasetVersion))) - .then((files) => FileJSDataverseRepository.getAllWithDownloadCount(files)) - .then((files) => FileJSDataverseRepository.getAllWithThumbnail(files)) + .then((jsFiles) => + Promise.all([ + jsFiles, + FileJSDataverseRepository.getAllDownloadCount(jsFiles), + FileJSDataverseRepository.getAllThumbnails(jsFiles), + FileJSDataverseRepository.getAllTabularData(jsFiles) + ]) + ) + .then(([jsFiles, downloadCounts, thumbnails, jsTabularData]) => + jsFiles.map((jsFile, index) => + JSFileMapper.toFile( + jsFile, + datasetVersion, + downloadCounts[index], + thumbnails[index], + jsTabularData[index] + ) + ) + ) .catch((error: ReadError) => { throw new Error(error.message) }) } - private static getAllWithDownloadCount(files: File[]): Promise { + private static getAllTabularData( + jsFiles: JSFile[] + ): Promise<(JSFileTabularData[] | undefined)[]> { return Promise.all( - files.map((file) => - FileJSDataverseRepository.getDownloadCountById(file.id, file.version.publishingStatus).then( - (downloadCount) => { - file.downloadCount = downloadCount - return file - } - ) + jsFiles.map((jsFile) => + jsFile.tabularData ? getFileDataTables.execute(jsFile.id) : undefined ) ) } - private static getDownloadCountById( - id: number, - publishingStatus: FilePublishingStatus - ): Promise { - if (publishingStatus === FilePublishingStatus.RELEASED) { - return getFileDownloadCount.execute(id).then((downloadCount) => Number(downloadCount)) - } - return Promise.resolve(0) - } - - private static getAllWithThumbnail(files: File[]): Promise { + private static getAllDownloadCount(jsFiles: JSFile[]): Promise { return Promise.all( - files.map((file) => - FileJSDataverseRepository.getThumbnailById(file.id).then((thumbnail) => { - file.thumbnail = thumbnail - return file - }) + jsFiles.map((jsFile) => + jsFile.publicationDate + ? getFileDownloadCount.execute(jsFile.id).then((downloadCount) => Number(downloadCount)) + : 0 ) ) } + private static getAllThumbnails(jsFiles: JSFile[]): Promise<(string | undefined)[]> { + return Promise.all(jsFiles.map((jsFile) => this.getThumbnailById(jsFile.id))) + } + private static getThumbnailById(id: number): Promise { return fetch(`${this.DATAVERSE_BACKEND_URL}/api/access/datafile/${id}?imageThumb=400`) .then((response) => { diff --git a/src/files/infrastructure/mappers/JSFileMapper.ts b/src/files/infrastructure/mappers/JSFileMapper.ts index 3956b6d25..aa5ec32ed 100644 --- a/src/files/infrastructure/mappers/JSFileMapper.ts +++ b/src/files/infrastructure/mappers/JSFileMapper.ts @@ -11,6 +11,7 @@ import { FilePublishingStatus, FileSize, FileSizeUnit, + FileTabularData, FileType, FileVersion } from '../../domain/models/File' @@ -22,7 +23,8 @@ import { FileContentTypeCount as JSFileContentTypeCount, FileCategoryNameCount as JSFileCategoryNameCount, FileAccessStatusCount as JSFileAccessStatusCount, - FileAccessStatus as JSFileAccessStatus + FileAccessStatus as JSFileAccessStatus, + FileDataTable as JSFileTabularData } from '@iqss/dataverse-client-javascript' import { DatasetPublishingStatus, DatasetVersion } from '../../../dataset/domain/models/Dataset' import { FileUserPermissions } from '../../domain/models/FileUserPermissions' @@ -35,7 +37,13 @@ import { import { FileAccessOption, FileTag } from '../../domain/models/FileCriteria' export class JSFileMapper { - static toFile(jsFile: JSFile, datasetVersion: DatasetVersion): File { + static toFile( + jsFile: JSFile, + datasetVersion: DatasetVersion, + downloadsCount: number, + thumbnail?: string, + jsTabularData?: JSFileTabularData[] + ): File { return new File( this.toFileId(jsFile.id), this.toFileVersion(jsFile.version, datasetVersion, jsFile.publicationDate), @@ -44,15 +52,15 @@ export class JSFileMapper { this.toFileType(jsFile.contentType, jsFile.originalFormatLabel), this.toFileSize(jsFile.sizeBytes), this.toFileDate(jsFile.creationDate, jsFile.publicationDate, jsFile.embargo), - this.toFileDownloads(), + this.toFileDownloads(downloadsCount), this.toFileLabels(jsFile.categories, jsFile.tabularTags), - false, // TODO - Implement this when it is added to js-dataverse + this.toFileIsDeleted(jsFile.deleted), { status: FileIngestStatus.NONE }, // TODO - Implement this when it is added to js-dataverse this.toFileChecksum(jsFile.checksum), - this.toFileThumbnail(), + this.toFileThumbnail(thumbnail), this.toFileDirectory(jsFile.directoryLabel), this.toFileEmbargo(jsFile.embargo), - this.toFileTabularData(), + this.toFileTabularData(jsTabularData), this.toFileDescription(jsFile.description) ) } @@ -132,8 +140,8 @@ export class JSFileMapper { throw new Error('File date not found') } - static toFileDownloads(): number { - return 0 // This is always 0 because the downloads come from a different endpoint + static toFileDownloads(downloadsCount: number): number { + return downloadsCount } static toFileLabels(jsFileCategories?: string[], jsFileTabularTags?: string[]): FileLabel[] { @@ -159,8 +167,8 @@ export class JSFileMapper { return undefined } - static toFileThumbnail(): undefined { - return undefined // This is always undefined because the thumbnails come from a different endpoint + static toFileThumbnail(thumbnail?: string): string | undefined { + return thumbnail } static toFileDirectory(jsFileDirectory: string | undefined): string | undefined { @@ -174,8 +182,15 @@ export class JSFileMapper { return undefined } - static toFileTabularData(): undefined { - return undefined // This is always undefined because the tabular data comes from a different endpoint + static toFileTabularData(jsTabularData?: JSFileTabularData[]): FileTabularData | undefined { + if (jsTabularData === undefined) { + return undefined + } + return { + variablesCount: jsTabularData[0].varQuantity ?? 0, + observationsCount: jsTabularData[0].caseQuantity ?? 0, + unf: jsTabularData[0].UNF + } } static toFileDescription(jsFileDescription?: string): string | undefined { @@ -230,4 +245,8 @@ export class JSFileMapper { return FileAccessOption.EMBARGOED_RESTRICTED } } + + static toFileIsDeleted(jsFileIsDeleted: boolean | undefined): boolean { + return jsFileIsDeleted ?? false + } } diff --git a/src/sections/dataset/Dataset.tsx b/src/sections/dataset/Dataset.tsx index c5444efb3..8095e0ae8 100644 --- a/src/sections/dataset/Dataset.tsx +++ b/src/sections/dataset/Dataset.tsx @@ -14,6 +14,8 @@ import { DatasetActionButtons } from './dataset-action-buttons/DatasetActionButt import { useDataset } from './DatasetContext' import { useEffect } from 'react' import { DatasetAlerts } from './dataset-alerts/DatasetAlerts' +import { useNotImplementedModal } from '../not-implemented/NotImplementedModalContext' +import { NotImplementedModal } from '../not-implemented/NotImplementedModal' interface DatasetProps { fileRepository: FileRepository @@ -23,6 +25,7 @@ export function Dataset({ fileRepository }: DatasetProps) { const { setIsLoading } = useLoading() const { dataset, isLoading } = useDataset() const { t } = useTranslation('dataset') + const { hideModal, isModalOpen } = useNotImplementedModal() useEffect(() => { setIsLoading(isLoading) @@ -34,6 +37,7 @@ export function Dataset({ fileRepository }: DatasetProps) { return ( <> + {!dataset ? ( ) : ( diff --git a/src/sections/dataset/DatasetFactory.tsx b/src/sections/dataset/DatasetFactory.tsx index a256f892b..7cbdecc0b 100644 --- a/src/sections/dataset/DatasetFactory.tsx +++ b/src/sections/dataset/DatasetFactory.tsx @@ -11,6 +11,7 @@ import { SettingJSDataverseRepository } from '../../settings/infrastructure/Sett import { FilePermissionsProvider } from '../file/file-permissions/FilePermissionsProvider' import { SettingsProvider } from '../settings/SettingsProvider' import { DatasetProvider } from './DatasetProvider' +import { NotImplementedModalProvider } from '../not-implemented/NotImplementedModalProvider' import { AlertProvider } from '../alerts/AlertProvider' const datasetRepository = new DatasetJSDataverseRepository() @@ -23,13 +24,15 @@ export class DatasetFactory { return ( - - - - - - - + + + + + + + + + ) diff --git a/src/sections/dataset/dataset-action-buttons/edit-dataset-menu/EditDatasetMenu.tsx b/src/sections/dataset/dataset-action-buttons/edit-dataset-menu/EditDatasetMenu.tsx index 798e2d451..03faf3cd1 100644 --- a/src/sections/dataset/dataset-action-buttons/edit-dataset-menu/EditDatasetMenu.tsx +++ b/src/sections/dataset/dataset-action-buttons/edit-dataset-menu/EditDatasetMenu.tsx @@ -4,6 +4,7 @@ import { EditDatasetPermissionsMenu } from './EditDatasetPermissionsMenu' import { DeleteDatasetButton } from './DeleteDatasetButton' import { DeaccessionDatasetButton } from './DeaccessionDatasetButton' import { useTranslation } from 'react-i18next' +import { useNotImplementedModal } from '../../../not-implemented/NotImplementedModalContext' import { useSession } from '../../../session/SessionContext' interface EditDatasetMenuProps { @@ -16,10 +17,11 @@ export function EditDatasetMenu({ dataset }: EditDatasetMenuProps) { if (!user || !dataset.permissions.canUpdateDataset) { return <> } - + const { showModal } = useNotImplementedModal() const { t } = useTranslation('dataset') return ( { + // TODO - Implement upload files + showModal() + } + const { showModal } = useNotImplementedModal() + if ( !user || !dataset.isReleased || @@ -19,7 +27,9 @@ export function LinkDatasetButton({ dataset }: LinkDatasetButtonProps) { return ( - + ) } diff --git a/src/sections/dataset/dataset-action-buttons/publish-dataset-menu/PublishDatasetMenu.tsx b/src/sections/dataset/dataset-action-buttons/publish-dataset-menu/PublishDatasetMenu.tsx index ac7f9cc0d..6ea678467 100644 --- a/src/sections/dataset/dataset-action-buttons/publish-dataset-menu/PublishDatasetMenu.tsx +++ b/src/sections/dataset/dataset-action-buttons/publish-dataset-menu/PublishDatasetMenu.tsx @@ -2,6 +2,7 @@ import { Dataset, DatasetPublishingStatus } from '../../../../dataset/domain/mod import { DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system' import { ChangeCurationStatusMenu } from './ChangeCurationStatusMenu' import { useTranslation } from 'react-i18next' +import { useNotImplementedModal } from '../../../not-implemented/NotImplementedModalContext' import { useSession } from '../../../session/SessionContext' interface PublishDatasetMenuProps { @@ -20,10 +21,17 @@ export function PublishDatasetMenu({ dataset }: PublishDatasetMenuProps) { } const { t } = useTranslation('dataset') + const handleSelect = () => { + // TODO - Implement upload files + showModal() + } + const { showModal } = useNotImplementedModal() + return ( { // TODO - Implement upload files + showModal() } - + const { showModal } = useNotImplementedModal() if (!user || !dataset?.permissions.canUpdateDataset) { return <> } return ( + + + ) +} diff --git a/src/sections/not-implemented/NotImplementedModalContext.ts b/src/sections/not-implemented/NotImplementedModalContext.ts new file mode 100644 index 000000000..81165cc8a --- /dev/null +++ b/src/sections/not-implemented/NotImplementedModalContext.ts @@ -0,0 +1,15 @@ +import { createContext, useContext } from 'react' + +interface NotImplementedModalContextProps { + showModal: () => void + hideModal: () => void + isModalOpen: boolean +} + +export const NotImplementedModal = createContext({ + isModalOpen: false, + showModal: /* istanbul ignore next */ () => {}, + hideModal: /* istanbul ignore next */ () => {} +}) + +export const useNotImplementedModal = () => useContext(NotImplementedModal) diff --git a/src/sections/not-implemented/NotImplementedModalProvider.tsx b/src/sections/not-implemented/NotImplementedModalProvider.tsx new file mode 100644 index 000000000..3a18ce32e --- /dev/null +++ b/src/sections/not-implemented/NotImplementedModalProvider.tsx @@ -0,0 +1,14 @@ +import { useState, PropsWithChildren } from 'react' +import { NotImplementedModal } from './NotImplementedModalContext' + +export function NotImplementedModalProvider({ children }: PropsWithChildren) { + const [isModalOpen, setIsModalOpen] = useState(false) + const showModal = () => setIsModalOpen(true) + const hideModal = () => setIsModalOpen(false) + + return ( + + {children} + + ) +} diff --git a/src/stories/WithNotImplementedModal.tsx b/src/stories/WithNotImplementedModal.tsx new file mode 100644 index 000000000..2f91d7d7b --- /dev/null +++ b/src/stories/WithNotImplementedModal.tsx @@ -0,0 +1,10 @@ +import { StoryFn } from '@storybook/react' +import { NotImplementedModalProvider } from '../sections/not-implemented/NotImplementedModalProvider' + +export const WithNotImplementedModal = (Story: StoryFn) => { + return ( + + + + ) +} diff --git a/src/stories/dataset/Dataset.stories.tsx b/src/stories/dataset/Dataset.stories.tsx index 0bfd77785..85fe96332 100644 --- a/src/stories/dataset/Dataset.stories.tsx +++ b/src/stories/dataset/Dataset.stories.tsx @@ -16,6 +16,7 @@ import { WithDatasetNotFound } from './WithDatasetNotFound' import { WithDatasetLoading } from './WithDatasetLoading' import { WithLoggedInUser } from '../WithLoggedInUser' import { WithAlerts } from '../WithAlerts' +import { WithNotImplementedModal } from '../WithNotImplementedModal' const meta: Meta = { title: 'Pages/Dataset', @@ -31,16 +32,28 @@ export default meta type Story = StoryObj export const Default: Story = { - decorators: [WithLayout, WithDataset, WithFilePermissionsDenied], + decorators: [WithLayout, WithDataset, WithFilePermissionsDenied, WithNotImplementedModal], render: () => } export const DraftWithAllDatasetPermissions: Story = { - decorators: [WithLayout, WithDatasetDraftAsOwner, WithLoggedInUser, WithFilePermissionsGranted], + decorators: [ + WithLayout, + WithDatasetDraftAsOwner, + WithLoggedInUser, + WithFilePermissionsGranted, + WithNotImplementedModal + ], render: () => } export const LoggedInAsOwner: Story = { - decorators: [WithDataset, WithLayout, WithLoggedInUser, WithFilePermissionsGranted], + decorators: [ + WithDataset, + WithLayout, + WithLoggedInUser, + WithFilePermissionsGranted, + WithNotImplementedModal + ], render: () => } diff --git a/src/stories/dataset/dataset-citation/DatasetCitation.stories.tsx b/src/stories/dataset/dataset-citation/DatasetCitation.stories.tsx index dc89d2f59..cd7d511d4 100644 --- a/src/stories/dataset/dataset-citation/DatasetCitation.stories.tsx +++ b/src/stories/dataset/dataset-citation/DatasetCitation.stories.tsx @@ -34,7 +34,6 @@ export const Default: Story = { export const WithThumbnail: Story = { render: () => { const dataset = DatasetMother.createRealistic({ thumbnail: faker.image.imageUrl() }) - console.log(dataset) return (


diff --git a/src/stories/not-implemented/NotImplementedModal.stories.tsx b/src/stories/not-implemented/NotImplementedModal.stories.tsx new file mode 100644 index 000000000..8b8489011 --- /dev/null +++ b/src/stories/not-implemented/NotImplementedModal.stories.tsx @@ -0,0 +1,30 @@ +import { Meta, StoryObj } from '@storybook/react' +import { NotImplementedModal } from '../../sections/not-implemented/NotImplementedModal' +import { Button } from '@iqss/dataverse-design-system' +import { useNotImplementedModal } from '../../sections/not-implemented/NotImplementedModalContext' +import { WithNotImplementedModal } from '../WithNotImplementedModal' + +const meta: Meta = { + title: 'Sections/NotImplementedModal', + component: NotImplementedModal, + decorators: [WithNotImplementedModal] +} + +export default meta + +type Story = StoryObj + +function DefaultExample() { + const { showModal, hideModal, isModalOpen } = useNotImplementedModal() + return ( + <> + + + + + ) +} + +export const Default: Story = { + render: () => DefaultExample() +} diff --git a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx index 8322b7a71..e40d20a84 100644 --- a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx @@ -289,4 +289,23 @@ describe('FileCriteriaForm', () => { cy.findByLabelText('Search').should('have.value', 'test') }) + + it('renders the file criteria if there are less than 2 files but there is a filter applied', () => { + const criteria = new FileCriteria() + .withFilterByTag('document') + .withFilterByAccess(FileAccessOption.PUBLIC) + .withFilterByType('image/png') + + cy.customMount( + + ) + + cy.findByRole('button', { name: 'File Type: PNG Image' }).should('exist') + cy.findByRole('button', { name: 'Access: Public' }).should('exist') + cy.findByRole('button', { name: 'File Tags: Document' }).should('exist') + }) }) diff --git a/tests/component/sections/not-implemented/NotImplemented.spec.tsx b/tests/component/sections/not-implemented/NotImplemented.spec.tsx new file mode 100644 index 000000000..d7bc17c8c --- /dev/null +++ b/tests/component/sections/not-implemented/NotImplemented.spec.tsx @@ -0,0 +1,29 @@ +import { NotImplementedModal } from '../../../../src/sections/not-implemented/NotImplementedModal' +import { mount } from 'cypress/react18' + +describe('NotImplementedModal Component', () => { + it('renders the modal when show is true', () => { + // Mount the component with show set to true + mount( {}} />) + + // Check if the modal title is present + cy.findByText('Not Implemented').should('exist') + + // Check if the modal body has specific content + cy.findByText('This feature is not implemented yet in SPA.').should('exist') + }) + + it('closes the modal when the Close button is clicked', () => { + // A spy to check if handleClose is called + const handleClose = cy.spy().as('handleClose') + + // Mount the component with the spy as the handleClose prop + mount() + + // Click the Close button + cy.findByText('Close').click() + + // Check if handleClose was called + cy.get('@handleClose').should('have.been.calledOnce') + }) +}) diff --git a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx index 53dfc1f8f..46d3f9a3d 100644 --- a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx +++ b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx @@ -1,6 +1,6 @@ import { DatasetLabelValue } from '../../../../../src/dataset/domain/models/Dataset' import { TestsUtils } from '../../../shared/TestsUtils' -import { DatasetHelper } from '../../../shared/datasets/DatasetHelper' +import { DatasetHelper, DatasetResponse } from '../../../shared/datasets/DatasetHelper' import { FileHelper } from '../../../shared/files/FileHelper' import moment from 'moment-timezone' @@ -156,7 +156,20 @@ describe('Dataset', () => { }) it.skip('successfully loads a dataset deaccessioned', () => { - // TODO - Add test when deaccessioned endpoint works + // TODO - Implement once the getDatasetCitation includes deaccessioned datasets + cy.wrap(DatasetHelper.create()) + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + .then((dataset) => Promise.all([dataset, DatasetHelper.publish(dataset.persistentId)])) + .then(([dataset]: [DatasetResponse]) => { + return cy + .wait(2500) + .then(() => Promise.all([dataset, DatasetHelper.deaccession(dataset.id)])) + }) + .then(([dataset]: [DatasetResponse]) => { + cy.visit(`/spa/datasets?persistentId=${dataset.persistentId}`) + + cy.findByText(DatasetLabelValue.DEACCESSIONED).should('exist') + }) }) }) @@ -354,7 +367,7 @@ describe('Dataset', () => { }) }) - it('applies filters to the Files Table in the correct order', () => { + it.only('applies filters to the Files Table in the correct order', () => { const files = [ FileHelper.create('csv', { description: 'Some description', @@ -457,6 +470,25 @@ describe('Dataset', () => { cy.findByText('blob-3').should('not.exist') cy.get('table > tbody > tr').eq(0).should('contain', 'blob-5') cy.get('table > tbody > tr').eq(1).should('contain', 'blob-4') + + cy.findByLabelText('Search').clear().type('blob-5{enter}', { force: true }) + + cy.findByText('1 to 1 of 1 Files').should('exist') + cy.findByText('blob').should('not.exist') + cy.findByText('blob-1').should('not.exist') + cy.findByText('blob-2').should('not.exist') + cy.findByText('blob-3').should('not.exist') + cy.findByText('blob-4').should('not.exist') + cy.findByText('blob-5').should('exist') + + cy.findByLabelText('Search').clear().type('{enter}', { force: true }) + cy.findByText('1 to 3 of 3 Files').should('exist') + cy.findByText('blob').should('exist') + cy.findByText('blob-1').should('not.exist') + cy.findByText('blob-2').should('not.exist') + cy.findByText('blob-3').should('not.exist') + cy.findByText('blob-4').should('exist') + cy.findByText('blob-5').should('exist') }) }) diff --git a/tests/e2e-integration/integration/datasets/DatasetJSDataverseRepository.spec.ts b/tests/e2e-integration/integration/datasets/DatasetJSDataverseRepository.spec.ts index 1230aeeb8..1aa52c69a 100644 --- a/tests/e2e-integration/integration/datasets/DatasetJSDataverseRepository.spec.ts +++ b/tests/e2e-integration/integration/datasets/DatasetJSDataverseRepository.spec.ts @@ -249,6 +249,23 @@ describe('Dataset JSDataverse Repository', () => { }) }) + it.skip('gets the dataset by persistentId when the dataset is deaccessioned', async () => { + // TODO - Implement once the getDatasetCitation includes deaccessioned datasets + const datasetResponse = await DatasetHelper.create() + + await DatasetHelper.publish(datasetResponse.persistentId) + await TestsUtils.wait(1500) + + await DatasetHelper.deaccession(datasetResponse.id) + await datasetRepository.getByPersistentId(datasetResponse.persistentId).then((dataset) => { + if (!dataset) { + throw new Error('Dataset not found') + } + const datasetExpected = datasetData(dataset.persistentId, dataset.version.id) + + expect(dataset.getTitle()).to.deep.equal(datasetExpected.title) + }) + }) it('gets the dataset by persistentId when is locked', async () => { const datasetResponse = await DatasetHelper.create() await DatasetHelper.lock(datasetResponse.id, DatasetLockReason.FINALIZE_PUBLICATION) diff --git a/tests/e2e-integration/integration/files/FileJSDataverseRepository.spec.ts b/tests/e2e-integration/integration/files/FileJSDataverseRepository.spec.ts index 09c7de97c..d96da59e1 100644 --- a/tests/e2e-integration/integration/files/FileJSDataverseRepository.spec.ts +++ b/tests/e2e-integration/integration/files/FileJSDataverseRepository.spec.ts @@ -100,6 +100,7 @@ describe('File JSDataverse Repository', () => { expect(file.embargo).to.deep.equal(expectedFile.embargo) expect(file.tabularData).to.deep.equal(expectedFile.tabularData) expect(file.description).to.deep.equal(expectedFile.description) + expect(file.isDeleted).to.deep.equal(expectedFile.isDeleted) }) }) }) @@ -157,18 +158,15 @@ describe('File JSDataverse Repository', () => { }) it('gets all the files by dataset persistentId after dataset deaccession', async () => { - const dataset = await DatasetHelper.createWithFiles(FileHelper.createMany(3)).then( - (datasetResponse) => datasetRepository.getByPersistentId(datasetResponse.persistentId) - ) - if (!dataset) throw new Error('Dataset not found') + const datasetResponse = await DatasetHelper.createWithFiles(FileHelper.createMany(3)) - await DatasetHelper.publish(dataset.persistentId) - await TestsUtils.waitForNoLocks(dataset.persistentId) // Wait for the dataset to be published + await DatasetHelper.publish(datasetResponse.persistentId) + await TestsUtils.waitForNoLocks(datasetResponse.persistentId) // Wait for the dataset to be published + + const dataset = await datasetRepository.getByPersistentId(datasetResponse.persistentId) + if (!dataset) throw new Error('Dataset not found') - DatasetHelper.deaccession(dataset.persistentId) - await TestsUtils.wait(1500) // Wait for the dataset to be deaccessioned - await TestsUtils.wait(1500) // Wait for the dataset to be deaccessioned - await TestsUtils.wait(1500) // Wait for the dataset to be deaccessioned + await DatasetHelper.deaccession(datasetResponse.id) await fileRepository .getAllByDatasetPersistentId( @@ -263,7 +261,6 @@ describe('File JSDataverse Repository', () => { await fileRepository .getAllByDatasetPersistentId(dataset.persistentId, dataset.version) .then((files) => { - console.log(files) expect(files[0].thumbnail).to.not.be.undefined }) }) @@ -291,8 +288,7 @@ describe('File JSDataverse Repository', () => { }) }) - it.skip('gets all the files by dataset persistentId when files are tabular data', async () => { - // TODO - Implement this when isTabularData flag is added to js-dataverse response + it('gets all the files by dataset persistentId when files are tabular data', async () => { const datasetResponse = await DatasetHelper.createWithFiles(FileHelper.createMany(1, 'csv')) if (!datasetResponse.files) throw new Error('Files not found') @@ -303,12 +299,17 @@ describe('File JSDataverse Repository', () => { .getAllByDatasetPersistentId(dataset.persistentId, dataset.version) .then((files) => { const expectedTabularData = { - variablesCount: 1, - observationsCount: 0, - unf: 'some' + variablesCount: 7, + observationsCount: 10 } files.forEach((file) => { - expect(file.tabularData).to.deep.equal(expectedTabularData) + expect(file.tabularData?.variablesCount).to.deep.equal( + expectedTabularData.variablesCount + ) + expect(file.tabularData?.observationsCount).to.deep.equal( + expectedTabularData.observationsCount + ) + expect(file.tabularData?.unf).to.not.be.undefined }) }) }) @@ -436,6 +437,29 @@ describe('File JSDataverse Repository', () => { expect(files.length).to.equal(1) }) }) + + it.skip('gets all the files when they are deleted', async () => { + // This test is failing because js-dataverse deleted property always returns undefined + // TODO: Remove the skip once the issue is fixed + const datasetResponse = await DatasetHelper.createWithFiles(FileHelper.createMany(1)) + + await DatasetHelper.publish(datasetResponse.persistentId) + await TestsUtils.wait(2000) // Wait for the dataset to be published + + const dataset = await datasetRepository.getByPersistentId(datasetResponse.persistentId) + if (!dataset) throw new Error('Dataset not found') + + if (!datasetResponse.files) throw new Error('Files not found') + datasetResponse.files.map((file) => FileHelper.delete(file.id)) + + await fileRepository + .getAllByDatasetPersistentId(dataset.persistentId, dataset.version) + .then((files) => { + files.forEach((file) => { + expect(file.isDeleted).to.equal(true) + }) + }) + }) }) describe('Get file user permissions by id', () => { diff --git a/tests/e2e-integration/shared/datasets/DatasetHelper.ts b/tests/e2e-integration/shared/datasets/DatasetHelper.ts index 962dfa4d5..e46519395 100644 --- a/tests/e2e-integration/shared/datasets/DatasetHelper.ts +++ b/tests/e2e-integration/shared/datasets/DatasetHelper.ts @@ -40,23 +40,14 @@ export class DatasetHelper extends DataverseApiHelper { return { ...response, persistentId } } - static deaccession(persistentId: string) { - return cy - .visit(`/dataset.xhtml?persistentId=${persistentId}`) - .get('#editDataSet') - .click() - .get('#datasetForm\\:deaccessionDatasetLink') - .click() - .get('#datasetForm\\:reasonOptions_label') - .click() - .get('#datasetForm\\:reasonOptions_2') - .click() - .get('#datasetForm\\:reasonForDeaccession') - .type('Test deaccession') - .get('#datasetForm\\:j_idt2181') - .click() - .get('#datasetForm\\:deaccessionConfirmation_content > div > input') - .click() + static deaccession(id: string) { + return this.request<{ status: string }>( + `/datasets/${id}/versions/:latest-published/deaccession`, + 'POST', + { + deaccessionReason: 'Description of the deaccession reason.' + } + ) } static async createPrivateUrl(id: string): Promise<{ diff --git a/tests/e2e-integration/shared/files/FileHelper.ts b/tests/e2e-integration/shared/files/FileHelper.ts index 8c29b7fb7..7ae2c1b33 100644 --- a/tests/e2e-integration/shared/files/FileHelper.ts +++ b/tests/e2e-integration/shared/files/FileHelper.ts @@ -152,4 +152,8 @@ export class FileHelper extends DataverseApiHelper { 'multipart/form-data' ) } + + static async delete(id: number) { + return this.request(`/files/${id}`, 'DELETE') + } }