From 93b75c822f5a0e787aaf55288d7c9f99bbc920d9 Mon Sep 17 00:00:00 2001 From: Chris Wilkinson Date: Mon, 4 Oct 2021 12:17:43 +0100 Subject: [PATCH] test(integration): add cases for submitting rapid reviews through the API Adds test cases for submitting rapid reviews as an authenticated API user and an anonymous user. Only one of the cases fails, as it's possible to submit more than once. Refs #388, #408 --- integration/playwright.config.ts | 4 + .../src/api/submitting-a-rapid-review.spec.ts | 94 +++++++++++++++++++ .../no-api-key-API-linux.json | 3 + .../success-API-linux.json | 27 ++++++ integration/src/api/test.ts | 31 ++++++ integration/src/api/utils.ts | 20 ++++ integration/src/fixtures.ts | 10 ++ 7 files changed, 189 insertions(+) create mode 100644 integration/src/api/submitting-a-rapid-review.spec.ts create mode 100644 integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/no-api-key-API-linux.json create mode 100644 integration/src/api/submitting-a-rapid-review.spec.ts-snapshots/success-API-linux.json create mode 100644 integration/src/api/test.ts create mode 100644 integration/src/api/utils.ts 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,