diff --git a/integration/playwright.config.ts b/integration/playwright.config.ts index 4339300c..f8111cd5 100644 --- a/integration/playwright.config.ts +++ b/integration/playwright.config.ts @@ -29,6 +29,10 @@ const config: PlaywrightTestConfig = { ...devices['iPhone 11'], }, }, + { + name: 'API', + testDir: 'src/api', + }, ], }; diff --git a/integration/src/api/submitting-a-rapid-review.spec.ts b/integration/src/api/submitting-a-rapid-review.spec.ts new file mode 100644 index 00000000..739dd438 --- /dev/null +++ b/integration/src/api/submitting-a-rapid-review.spec.ts @@ -0,0 +1,94 @@ +import { expect, test } from './test'; +import { jsonBody } from './utils'; + +test.asAnAuthenticatedAPIUser( + 'can submit a rapid review', + async ({ apiFetch, preprint }) => { + const response = await apiFetch(`/api/v2/rapid-reviews`, { + method: 'POST', + body: JSON.stringify({ + preprint: preprint.uuid, + ynNovel: 'N/A', + ynFuture: 'N/A', + ynReproducibility: 'N/A', + ynMethods: 'N/A', + ynCoherent: 'N/A', + ynLimitations: 'N/A', + ynEthics: 'N/A', + ynNewData: 'N/A', + ynAvailableData: 'N/A', + ynAvailableCode: 'N/A', + ynRecommend: 'N/A', + ynPeerReview: 'N/A', + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + expect(response.status).toBe(201); + expect(await jsonBody(response)).toMatchSnapshot('success.json'); + }, +); + +test.asAnAnonymousAPIUser( + 'not allowed to submit a rapid review', + async ({ fetch, preprint }) => { + const response = await fetch(`/api/v2/rapid-reviews`, { + method: 'POST', + body: JSON.stringify({ + preprint: preprint.uuid, + ynNovel: 'N/A', + ynFuture: 'N/A', + ynReproducibility: 'N/A', + ynMethods: 'N/A', + ynCoherent: 'N/A', + ynLimitations: 'N/A', + ynEthics: 'N/A', + ynNewData: 'N/A', + ynAvailableData: 'N/A', + ynAvailableCode: 'N/A', + ynRecommend: 'N/A', + ynPeerReview: 'N/A', + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + expect(response.status).toBe(403); + expect(await jsonBody(response)).toMatchSnapshot('no-api-key.json'); + }, +); + +test.asAnAuthenticatedAPIUser( + 'not allowed to submit multiple rapid reviews', + async ({ apiFetch, preprint, rapidReview }, { fixme }) => { + const response = await apiFetch(`/api/v2/rapid-reviews`, { + method: 'POST', + body: JSON.stringify({ + preprint: preprint.uuid, + ynNovel: 'N/A', + ynFuture: 'N/A', + ynReproducibility: 'N/A', + ynMethods: 'N/A', + ynCoherent: 'N/A', + ynLimitations: 'N/A', + ynEthics: 'N/A', + ynNewData: 'N/A', + ynAvailableData: 'N/A', + ynAvailableCode: 'N/A', + ynRecommend: 'N/A', + ynPeerReview: 'N/A', + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + fixme(true, 'A successful response is returned'); + + expect(response.status).toBe(403); + expect(await jsonBody(response)).toMatchSnapshot('duplicate.json'); + }, +); diff --git a/integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/no-api-key-API-linux.json b/integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/no-api-key-API-linux.json new file mode 100644 index 00000000..a7d96977 --- /dev/null +++ b/integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/no-api-key-API-linux.json @@ -0,0 +1,3 @@ +{ + "message": "Access Denied - You don't have permission to: access private pages" +} diff --git a/integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/success-API-linux.json b/integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/success-API-linux.json new file mode 100644 index 00000000..203d0004 --- /dev/null +++ b/integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/success-API-linux.json @@ -0,0 +1,27 @@ +{ + "status": 201, + "message": "created", + "data": [ + { + "uuid": "...", + "createdAt": "...", + "updatedAt": "...", + "isPublished": false, + "isFlagged": false, + "ynNovel": "N/A", + "ynFuture": "N/A", + "ynReproducibility": "N/A", + "ynMethods": "N/A", + "ynCoherent": "N/A", + "ynLimitations": "N/A", + "ynEthics": "N/A", + "ynNewData": "N/A", + "ynRecommend": "N/A", + "ynPeerReview": "N/A", + "ynAvailableCode": "N/A", + "ynAvailableData": "N/A", + "author": 0, + "preprint": 0 + } + ] +} diff --git a/integration/src/api/test.ts b/integration/src/api/test.ts new file mode 100644 index 00000000..fa7cacb6 --- /dev/null +++ b/integration/src/api/test.ts @@ -0,0 +1,31 @@ +import { test as baseTest } from '@playwright/test'; +import { + dataFixtures, + fakerFixtures, + httpFixtures, + userDataFixtures, + userFixtures, +} from '../fixtures'; + +const dataTest = baseTest + .extend(fakerFixtures) + .extend(httpFixtures) + .extend(dataFixtures); + +const asAnAnonymousAPIUser = dataTest; + +const asAnAuthenticatedAPIUser = dataTest + .extend(userFixtures) + .extend(userDataFixtures) + .extend({ + storageState: async ({}, use) => { + await use('state/logged-in-user.json'); + }, + }); + +export const test = { + asAnAnonymousAPIUser, + asAnAuthenticatedAPIUser, +}; + +export { expect } from '@playwright/test'; diff --git a/integration/src/api/utils.ts b/integration/src/api/utils.ts new file mode 100644 index 00000000..72643378 --- /dev/null +++ b/integration/src/api/utils.ts @@ -0,0 +1,20 @@ +import { Body } from 'node-fetch'; + +export async function jsonBody(message: T): Promise { + return Buffer.from( + JSON.stringify(await message.json(), jsonReplacer, 2) + '\n', + ); +} + +function jsonReplacer(key: string, value: unknown) { + if (['author', 'createdAt', 'preprint', 'updatedAt', 'uuid'].includes(key)) { + switch (typeof value) { + case 'number': + return 0; + default: + return '...'; + } + } + + return value; +} diff --git a/integration/src/fixtures.ts b/integration/src/fixtures.ts index cf906fef..8aa413bd 100644 --- a/integration/src/fixtures.ts +++ b/integration/src/fixtures.ts @@ -42,6 +42,7 @@ type DataFixtures = { }; type UserFixtures = { + apiFetch: Fetch; apiHeaders: AuthHeaders; apiKey: ApiKey; user: User; @@ -116,6 +117,15 @@ export const userFixtures: Fixtures< {}, HttpFixtures & PlaywrightTestArgs & PlaywrightTestOptions > = { + apiFetch: async ( + // Types needed due to https://github.com/microsoft/playwright/issues/9125 + { apiHeaders, fetch }: PlaywrightTestOptions & HttpFixtures & UserFixtures, + use: (r: Fetch) => Promise, + ) => { + await use((url, init = {}) => + fetch(url, { ...init, headers: { ...apiHeaders, ...init.headers } }), + ); + }, apiHeaders: async ({ apiKey }, use) => { await use({ 'X-API-App': apiKey.app,