;
diff --git a/packages/api/test/cli/codegen/languages/typescript.test.ts b/packages/api/test/cli/codegen/languages/typescript.test.ts
index 7386b408..d1c6889b 100644
--- a/packages/api/test/cli/codegen/languages/typescript.test.ts
+++ b/packages/api/test/cli/codegen/languages/typescript.test.ts
@@ -3,6 +3,8 @@ import type { TSGeneratorOptions } from '../../../../src/cli/codegen/languages/t
import { promises as fs } from 'fs';
import path from 'path';
+import { responses as mockResponse } from '@api/test-utils/fetch-mock';
+import loadSpec from '@api/test-utils/load-spec';
import fetchMock from 'fetch-mock';
import Oas from 'oas';
import uniqueTempDir from 'unique-temp-dir';
@@ -11,8 +13,6 @@ import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest';
import TSGenerator from '../../../../src/cli/codegen/languages/typescript';
import Storage from '../../../../src/cli/storage';
import * as packageInfo from '../../../../src/packageInfo';
-import { responses as mockResponse } from '../../../helpers/fetch-mock';
-import loadSpec from '../../../helpers/load-spec';
function assertSDKFixture(file: string, fixture: string, opts: TSGeneratorOptions = {}) {
return async () => {
@@ -121,12 +121,12 @@ describe('typescript', () => {
describe('#generator', () => {
it(
'should generate typescript (by default)',
- assertSDKFixture('../../../__fixtures__/definitions/simple.json', 'simple-ts'),
+ assertSDKFixture('@api/test-utils/definitions/simple.json', 'simple-ts'),
);
it(
'should be able to generate valid TS when a body is optional but metadata isnt',
- assertSDKFixture('../../../__fixtures__/definitions/optional-payload.json', 'optional-payload'),
+ assertSDKFixture('@api/test-utils/definitions/optional-payload.json', 'optional-payload'),
);
it('should work against the petstore', assertSDKFixture('@readme/oas-examples/3.0/json/petstore.json', 'petstore'));
@@ -136,30 +136,30 @@ describe('typescript', () => {
// This SDK only has an `index.ts` as it has no schemas.
it(
'should handle some quirky `operationId` cases',
- assertSDKFixture('../../../__fixtures__/definitions/operationid-quirks.json', 'operationid-quirks'),
+ assertSDKFixture('@api/test-utils/definitions/operationid-quirks.json', 'operationid-quirks'),
);
it(
'should handle `title` props that start with a number',
- assertSDKFixture('../../../__fixtures__/definitions/response-title-quirks.json', 'response-title-quirks'),
+ assertSDKFixture('@api/test-utils/definitions/response-title-quirks.json', 'response-title-quirks'),
);
it.todo('should handle a operations with a `default` response');
it(
'should handle an api that has discriminators and no operation ids',
- assertSDKFixture('../../../__fixtures__/definitions/alby.json', 'alby'),
+ assertSDKFixture('@api/test-utils/definitions/alby.json', 'alby'),
);
describe('javascript generation', () => {
it(
'should generate a CommonJS library',
- assertSDKFixture('../../../__fixtures__/definitions/simple.json', 'simple-js-cjs', { outputJS: true }),
+ assertSDKFixture('@api/test-utils/definitions/simple.json', 'simple-js-cjs', { outputJS: true }),
);
it(
'should generate am ESM library',
- assertSDKFixture('../../../__fixtures__/definitions/simple.json', 'simple-js-esm', {
+ assertSDKFixture('@api/test-utils/definitions/simple.json', 'simple-js-esm', {
outputJS: true,
compilerTarget: 'esm',
}),
diff --git a/packages/api/test/cli/codegen/languages/typescript/smoketest.test.ts b/packages/api/test/cli/codegen/languages/typescript/smoketest.test.ts
index 97840bbd..ee9c65be 100644
--- a/packages/api/test/cli/codegen/languages/typescript/smoketest.test.ts
+++ b/packages/api/test/cli/codegen/languages/typescript/smoketest.test.ts
@@ -15,12 +15,12 @@
* @example Everything
* npm run test:smoke
*/
+import realWorldAPIs from '@api/test-utils/datasets/real-world-apis.json';
import Oas from 'oas';
import OASNormalize from 'oas-normalize';
import { describe, it, expect } from 'vitest';
import TSGenerator from '../../../../../src/cli/codegen/languages/typescript';
-import realWorldAPIs from '../../../../datasets/real-world-apis.json';
// These APIs don't have any schemas so they should only be generating an `index.ts`.
const APIS_WITHOUT_SCHEMAS = ['poemist.com'];
diff --git a/packages/api/test/cli/fetcher.test.ts b/packages/api/test/cli/fetcher.test.ts
index 4dd32f80..22976622 100644
--- a/packages/api/test/cli/fetcher.test.ts
+++ b/packages/api/test/cli/fetcher.test.ts
@@ -1,11 +1,11 @@
import assert from 'assert';
import fs from 'fs/promises';
+import loadSpec from '@api/test-utils/load-spec';
import fetchMock from 'fetch-mock';
import { describe, beforeAll, it, expect } from 'vitest';
import Fetcher from '../../src/cli/fetcher';
-import loadSpec from '../helpers/load-spec';
let readmeSpec;
diff --git a/packages/api/test/cli/storage.test.ts b/packages/api/test/cli/storage.test.ts
index 6111fe46..9dad3aca 100644
--- a/packages/api/test/cli/storage.test.ts
+++ b/packages/api/test/cli/storage.test.ts
@@ -4,13 +4,13 @@ import assert from 'assert';
import fs from 'fs/promises';
import path from 'path';
+import loadSpec from '@api/test-utils/load-spec';
import fetchMock from 'fetch-mock';
import uniqueTempDir from 'unique-temp-dir';
import { describe, beforeAll, beforeEach, afterEach, it, expect } from 'vitest';
import Storage from '../../src/cli/storage';
import { PACKAGE_VERSION } from '../../src/packageInfo';
-import loadSpec from '../helpers/load-spec';
let petstoreSimple;
diff --git a/packages/api/test/global.d.ts b/packages/api/test/global.d.ts
index 89196dab..eace3e8b 100644
--- a/packages/api/test/global.d.ts
+++ b/packages/api/test/global.d.ts
@@ -1 +1 @@
-import './helpers/vitest.matchers';
+import '@api/test-utils/vitest.matchers';
diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json
index 513f48fa..c04ee9df 100644
--- a/packages/api/tsconfig.json
+++ b/packages/api/tsconfig.json
@@ -4,7 +4,7 @@
"baseUrl": "./src",
"declaration": true,
"esModuleInterop": true,
- "lib": ["dom", "dom.iterable", "es2020"],
+ "lib": ["DOM", "DOM.Iterable", "ES2020"],
"noImplicitAny": true,
"outDir": "dist/"
},
diff --git a/packages/api/vitest-smoketest.config.ts b/packages/api/vitest-smoketest.config.ts
index 5060a0bc..beaee9cd 100644
--- a/packages/api/vitest-smoketest.config.ts
+++ b/packages/api/vitest-smoketest.config.ts
@@ -4,7 +4,7 @@ import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
include: ['**/smoketest.test.ts'],
- setupFiles: ['./test/helpers/vitest.matchers.ts'],
+ setupFiles: ['@api/test-utils/vitest.matchers.ts'],
testTimeout: 20000,
},
});
diff --git a/packages/api/vitest.config.ts b/packages/api/vitest.config.ts
index 8363a45e..e5d0af9b 100644
--- a/packages/api/vitest.config.ts
+++ b/packages/api/vitest.config.ts
@@ -11,7 +11,7 @@ export default defineConfig({
'**/helpers/**',
'**/smoketest.test.ts',
],
- setupFiles: ['./test/helpers/vitest.matchers.ts'],
+ setupFiles: ['@api/test-utils/vitest.matchers.ts'],
testTimeout: 20000,
},
});
diff --git a/packages/core/.eslintrc b/packages/core/.eslintrc
new file mode 100644
index 00000000..7ea05f4f
--- /dev/null
+++ b/packages/core/.eslintrc
@@ -0,0 +1,13 @@
+{
+ "rules": {
+ "@typescript-eslint/no-explicit-any": "off", // @todo fix these eventually
+
+ "no-restricted-imports": [
+ "error",
+ {
+ "name": "fs/promises",
+ "message": "Please use `fs` instead as some client frameworks don't polyfill `fs/promises`."
+ }
+ ]
+ }
+}
diff --git a/packages/core/.gitignore b/packages/core/.gitignore
new file mode 100644
index 00000000..ef229ed3
--- /dev/null
+++ b/packages/core/.gitignore
@@ -0,0 +1,3 @@
+coverage/
+dist/
+node_modules/
diff --git a/packages/core/.npmignore b/packages/core/.npmignore
new file mode 100644
index 00000000..5324474f
--- /dev/null
+++ b/packages/core/.npmignore
@@ -0,0 +1,7 @@
+.api/
+coverage/
+test/
+.eslint*
+.gitignore
+.prettier*
+vitest*
diff --git a/packages/core/LICENSE b/packages/core/LICENSE
new file mode 100644
index 00000000..8e096215
--- /dev/null
+++ b/packages/core/LICENSE
@@ -0,0 +1,18 @@
+Copyright © 2023 ReadMe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/core/README.md b/packages/core/README.md
new file mode 100644
index 00000000..04746d4b
--- /dev/null
+++ b/packages/core/README.md
@@ -0,0 +1,16 @@
+# @api/core
+
+
+
+
+
+
+
+
+
+The magic behind api
🧙
+
+
+
+Check out api.readme.dev for more details.
+
diff --git a/packages/core/package.json b/packages/core/package.json
new file mode 100644
index 00000000..c0591b4f
--- /dev/null
+++ b/packages/core/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "@api/core",
+ "version": "1.0.0",
+ "description": "The magic behind `api` 🧙",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "lint:types": "tsc --noEmit",
+ "prebuild": "rm -rf dist/",
+ "prepack": "npm run build",
+ "test": "vitest run --coverage"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/readmeio/api.git",
+ "directory": "packages/core"
+ },
+ "homepage": "https://api.readme.dev",
+ "bugs": {
+ "url": "https://github.com/readmeio/api/issues"
+ },
+ "author": "Jon Ursenbach ",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "dependencies": {
+ "@readme/oas-to-har": "^20.1.1",
+ "caseless": "^0.12.0",
+ "datauri": "^4.1.0",
+ "fetch-har": "^10.0.0",
+ "get-stream": "^6.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "lodash.merge": "^4.6.2",
+ "oas": "^20.10.3",
+ "remove-undefined-objects": "^3.0.0"
+ },
+ "devDependencies": {
+ "@api/test-utils": "file:../test-utils",
+ "@readme/oas-examples": "^5.12.0",
+ "@types/caseless": "^0.12.3",
+ "@types/lodash.merge": "^4.6.7",
+ "@vitest/coverage-v8": "^0.34.4",
+ "fetch-mock": "^9.11.0",
+ "typescript": "^5.2.2",
+ "vitest": "^0.34.4"
+ },
+ "prettier": "@readme/eslint-config/prettier"
+}
diff --git a/packages/api/src/core/errors/fetchError.ts b/packages/core/src/errors/fetchError.ts
similarity index 100%
rename from packages/api/src/core/errors/fetchError.ts
rename to packages/core/src/errors/fetchError.ts
diff --git a/packages/api/src/core/index.ts b/packages/core/src/index.ts
similarity index 78%
rename from packages/api/src/core/index.ts
rename to packages/core/src/index.ts
index 09c2a71e..30d15a53 100644
--- a/packages/api/src/core/index.ts
+++ b/packages/core/src/index.ts
@@ -6,11 +6,11 @@ import oasToHar from '@readme/oas-to-har';
import fetchHar from 'fetch-har';
import FetchError from './errors/fetchError';
-import getJSONSchemaDefaults from './getJSONSchemaDefaults';
-import parseResponse from './parseResponse';
-import prepareAuth from './prepareAuth';
-import prepareParams from './prepareParams';
-import prepareServer from './prepareServer';
+import getJSONSchemaDefaults from './lib/getJSONSchemaDefaults';
+import parseResponse from './lib/parseResponse';
+import prepareAuth from './lib/prepareAuth';
+import prepareParams from './lib/prepareParams';
+import prepareServer from './lib/prepareServer';
export interface ConfigOptions {
/**
@@ -20,11 +20,11 @@ export interface ConfigOptions {
timeout?: number;
}
-export interface FetchResponse {
- data: data;
+export interface FetchResponse {
+ data: Data;
headers: Headers;
res: Response;
- status: status;
+ status: HTTPStatus;
}
// https://stackoverflow.com/a/39495173
@@ -81,13 +81,22 @@ export default class APICore {
return this;
}
- async fetch(path: string, method: HttpMethods, body?: unknown, metadata?: Record) {
+ async fetch(
+ path: string,
+ method: HttpMethods,
+ body?: unknown,
+ metadata?: Record,
+ ) {
const operation = this.spec.operation(path, method);
- return this.fetchOperation(operation, body, metadata);
+ return this.fetchOperation(operation, body, metadata);
}
- async fetchOperation(operation: Operation, body?: unknown, metadata?: Record) {
+ async fetchOperation(
+ operation: Operation,
+ body?: unknown,
+ metadata?: Record,
+ ) {
return prepareParams(operation, body, metadata).then(params => {
const data = { ...params };
@@ -104,12 +113,12 @@ export default class APICore {
// @ts-expect-error `this.auth` typing is off. FIXME
const har = oasToHar(this.spec, operation, data, prepareAuth(this.auth, operation));
- let timeoutSignal: any;
+ let timeoutSignal: NodeJS.Timeout;
const init: RequestInit = {};
if (this.config.timeout) {
const controller = new AbortController();
timeoutSignal = setTimeout(() => controller.abort(), this.config.timeout);
- init.signal = controller.signal as any;
+ init.signal = controller.signal;
}
return fetchHar(har as any, {
@@ -118,7 +127,7 @@ export default class APICore {
userAgent: this.userAgent,
})
.then(async (res: Response) => {
- const parsed = await parseResponse(res);
+ const parsed = await parseResponse(res);
if (res.status >= 400 && res.status <= 599) {
throw new FetchError(
diff --git a/packages/api/src/core/getJSONSchemaDefaults.ts b/packages/core/src/lib/getJSONSchemaDefaults.ts
similarity index 100%
rename from packages/api/src/core/getJSONSchemaDefaults.ts
rename to packages/core/src/lib/getJSONSchemaDefaults.ts
diff --git a/packages/api/src/core/parseResponse.ts b/packages/core/src/lib/parseResponse.ts
similarity index 75%
rename from packages/api/src/core/parseResponse.ts
rename to packages/core/src/lib/parseResponse.ts
index 7e1f53b9..48095fff 100644
--- a/packages/api/src/core/parseResponse.ts
+++ b/packages/core/src/lib/parseResponse.ts
@@ -2,13 +2,13 @@ import { utils } from 'oas';
const { matchesMimeType } = utils;
-export default async function getResponseBody(response: Response) {
+export default async function parseResponse(response: Response) {
const contentType = response.headers.get('Content-Type');
const isJSON = contentType && (matchesMimeType.json(contentType) || matchesMimeType.wildcard(contentType));
const responseBody = await response.text();
- let data = responseBody;
+ let data: any = responseBody;
if (isJSON) {
try {
data = JSON.parse(responseBody);
@@ -19,7 +19,7 @@ export default async function getResponseBody(response: Response) {
return {
data,
- status: response.status,
+ status: response.status as HTTPStatus,
headers: response.headers,
res: response,
};
diff --git a/packages/api/src/core/prepareAuth.ts b/packages/core/src/lib/prepareAuth.ts
similarity index 100%
rename from packages/api/src/core/prepareAuth.ts
rename to packages/core/src/lib/prepareAuth.ts
diff --git a/packages/api/src/core/prepareParams.ts b/packages/core/src/lib/prepareParams.ts
similarity index 100%
rename from packages/api/src/core/prepareParams.ts
rename to packages/core/src/lib/prepareParams.ts
diff --git a/packages/api/src/core/prepareServer.ts b/packages/core/src/lib/prepareServer.ts
similarity index 100%
rename from packages/api/src/core/prepareServer.ts
rename to packages/core/src/lib/prepareServer.ts
diff --git a/packages/core/test/.eslintrc b/packages/core/test/.eslintrc
new file mode 100644
index 00000000..716c33cc
--- /dev/null
+++ b/packages/core/test/.eslintrc
@@ -0,0 +1,3 @@
+{
+ "extends": "@readme/eslint-config/testing/vitest"
+}
diff --git a/packages/core/test/global.d.ts b/packages/core/test/global.d.ts
new file mode 100644
index 00000000..eace3e8b
--- /dev/null
+++ b/packages/core/test/global.d.ts
@@ -0,0 +1 @@
+import '@api/test-utils/vitest.matchers';
diff --git a/packages/api/test/core/index.test.ts b/packages/core/test/index.test.ts
similarity index 96%
rename from packages/api/test/core/index.test.ts
rename to packages/core/test/index.test.ts
index b889571a..57598803 100644
--- a/packages/api/test/core/index.test.ts
+++ b/packages/core/test/index.test.ts
@@ -1,14 +1,14 @@
import assert from 'assert';
+import { responses as mockResponse } from '@api/test-utils/fetch-mock';
+import loadSpec from '@api/test-utils/load-spec';
import datauri from 'datauri';
import fetchMock from 'fetch-mock';
import Oas from 'oas';
import { describe, beforeEach, afterEach, it, expect } from 'vitest';
-import APICore from '../../src/core';
-import FetchError from '../../src/core/errors/fetchError';
-import { responses as mockResponse } from '../helpers/fetch-mock';
-import loadSpec from '../helpers/load-spec';
+import APICore from '../src';
+import FetchError from '../src/errors/fetchError';
describe('APICore', () => {
let fileUploads: APICore;
@@ -88,11 +88,13 @@ describe('APICore', () => {
.fetch('/pets/{id}', 'delete', undefined, { id: petId })
.then(() => assert.fail())
.catch(err => {
+ /* eslint-disable vitest/no-conditional-expect */
expect(err).toBeInstanceOf(FetchError);
expect(err.status).toBe(404);
expect(err.data).toBe('Could not find that pet.');
expect(err.headers).toHaveHeader('content-type', /text\/plain/);
expect(err.res.constructor.name).toBe('Response');
+ /* eslint-enable vitest/no-conditional-expect */
});
});
});
@@ -238,7 +240,7 @@ describe('APICore', () => {
it('should support `image/png` requests', async () => {
fetchMock.post('https://httpbin.org/anything/image-png', mockResponse.datauri);
- const file = `${__dirname}/../__fixtures__/owlbert.png`;
+ const file = require.resolve('@api/test-utils/fixtures/owlbert.png');
const { data } = await fileUploads.fetch('/anything/image-png', 'post', file);
@@ -260,6 +262,7 @@ describe('APICore', () => {
};
const { data } = await parametersStyle.fetch('/anything/form-data/form', 'post', body);
+
expect(data.uri).toBe('/anything/form-data/form');
expect(data.requestBody.split(`${data.boundary}`).filter(Boolean)).toStrictEqual([
'\r\nContent-Disposition: form-data; name="primitive"\r\n\r\nstring\r\n',
@@ -276,7 +279,7 @@ describe('APICore', () => {
const body = {
orderId: 1234,
userId: 5678,
- documentFile: `${__dirname}/../__fixtures__/hello.txt`,
+ documentFile: require.resolve('@api/test-utils/fixtures/hello.txt'),
};
const { data } = await fileUploads.fetch('/anything/multipart-formdata', 'post', body);
@@ -293,7 +296,7 @@ describe('APICore', () => {
fetchMock.post('https://httpbin.org/anything/multipart-formdata', mockResponse.multipart);
const body = {
- documentFile: `${__dirname}/../__fixtures__/hello.jp.txt`,
+ documentFile: require.resolve('@api/test-utils/fixtures/hello.jp.txt'),
};
const { data } = await fileUploads.fetch('/anything/multipart-formdata', 'post', body);
diff --git a/packages/api/test/core/getJSONSchemaDefaults.test.ts b/packages/core/test/lib/getJSONSchemaDefaults.test.ts
similarity index 89%
rename from packages/api/test/core/getJSONSchemaDefaults.test.ts
rename to packages/core/test/lib/getJSONSchemaDefaults.test.ts
index f931df20..60799d30 100644
--- a/packages/api/test/core/getJSONSchemaDefaults.test.ts
+++ b/packages/core/test/lib/getJSONSchemaDefaults.test.ts
@@ -1,8 +1,8 @@
+import loadSpec from '@api/test-utils/load-spec';
import Oas from 'oas';
import { describe, it, expect } from 'vitest';
-import getJSONSchemaDefaults from '../../src/core/getJSONSchemaDefaults';
-import loadSpec from '../helpers/load-spec';
+import getJSONSchemaDefaults from '../../src/lib/getJSONSchemaDefaults';
describe('#getJSONSchemaDefaults()', () => {
it('should get defaults off an operation', async () => {
@@ -24,7 +24,7 @@ describe('#getJSONSchemaDefaults()', () => {
});
it('should be able to handle nested objects', async () => {
- const oas = await loadSpec('../__fixtures__/definitions/nested-defaults.json').then(Oas.init);
+ const oas = await loadSpec('@api/test-utils/definitions/nested-defaults.json').then(Oas.init);
await oas.dereference();
const operation = oas.operation('/pet', 'post');
diff --git a/packages/api/test/core/parseResponse.test.ts b/packages/core/test/lib/parseResponse.test.ts
similarity index 98%
rename from packages/api/test/core/parseResponse.test.ts
rename to packages/core/test/lib/parseResponse.test.ts
index 42e9fc89..623ca556 100644
--- a/packages/api/test/core/parseResponse.test.ts
+++ b/packages/core/test/lib/parseResponse.test.ts
@@ -1,6 +1,6 @@
import { describe, beforeEach, it, expect } from 'vitest';
-import parseResponse from '../../src/core/parseResponse';
+import parseResponse from '../../src/lib/parseResponse';
const responseBody = JSON.stringify({
id: 9205436248879918000,
diff --git a/packages/api/test/core/prepareAuth.test.ts b/packages/core/test/lib/prepareAuth.test.ts
similarity index 97%
rename from packages/api/test/core/prepareAuth.test.ts
rename to packages/core/test/lib/prepareAuth.test.ts
index 389c1a9f..66bd5fb3 100644
--- a/packages/api/test/core/prepareAuth.test.ts
+++ b/packages/core/test/lib/prepareAuth.test.ts
@@ -1,10 +1,10 @@
import type { OASDocument } from 'oas/dist/rmoas.types';
+import loadSpec from '@api/test-utils/load-spec';
import Oas from 'oas';
import { describe, beforeAll, it, expect } from 'vitest';
-import prepareAuth from '../../src/core/prepareAuth';
-import loadSpec from '../helpers/load-spec';
+import prepareAuth from '../../src/lib/prepareAuth';
let oas: Oas;
@@ -167,7 +167,7 @@ describe('#prepareAuth()', () => {
let securityMultipleOas;
beforeAll(async () => {
- authQuirksOas = await loadSpec(require.resolve('../__fixtures__/definitions/auth-quirks.json'));
+ authQuirksOas = await loadSpec(require.resolve('@api/test-utils/definitions/auth-quirks.json'));
securityMultipleOas = await loadSpec('@readme/oas-examples/3.0/json/security-multiple.json');
});
diff --git a/packages/api/test/core/prepareParams.test.ts b/packages/core/test/lib/prepareParams.test.ts
similarity index 95%
rename from packages/api/test/core/prepareParams.test.ts
rename to packages/core/test/lib/prepareParams.test.ts
index 6af34051..0838cd29 100644
--- a/packages/api/test/core/prepareParams.test.ts
+++ b/packages/core/test/lib/prepareParams.test.ts
@@ -1,11 +1,11 @@
import fs from 'fs';
+import payloadExamples from '@api/test-utils/definitions/payloads.json';
+import loadSpec from '@api/test-utils/load-spec';
import Oas from 'oas';
import { describe, beforeEach, it, expect } from 'vitest';
-import prepareParams from '../../src/core/prepareParams';
-import payloadExamples from '../__fixtures__/definitions/payloads.json';
-import loadSpec from '../helpers/load-spec';
+import prepareParams from '../../src/lib/prepareParams';
describe('#prepareParams', () => {
let fileUploads: Oas;
@@ -165,7 +165,7 @@ describe('#prepareParams', () => {
describe('image/png', () => {
it('should support a file path payload', async () => {
const operation = fileUploads.operation('/anything/image-png', 'post');
- const body = `${__dirname}/../__fixtures__/owlbert.png`;
+ const body = require.resolve('@api/test-utils/fixtures/owlbert.png');
const res = await prepareParams(operation, body);
expect(res.body).toContain('data:image/png;name=owlbert.png;base64,');
@@ -174,7 +174,7 @@ describe('#prepareParams', () => {
it('should support a file stream payload', async () => {
const operation = fileUploads.operation('/anything/image-png', 'post');
- const body = fs.createReadStream('./test/__fixtures__/owlbert.png');
+ const body = fs.createReadStream(require.resolve('@api/test-utils/fixtures/owlbert.png'));
const res = await prepareParams(operation, body);
expect(res.body).toContain('data:image/png;name=owlbert.png;base64,');
@@ -197,7 +197,7 @@ describe('#prepareParams', () => {
it('should handle when the file path is relative', async () => {
const operation = fileUploads.operation('/anything/multipart-formdata', 'post');
const body = {
- documentFile: './test/__fixtures__/owlbert.png',
+ documentFile: require.resolve('@api/test-utils/fixtures/owlbert.png'),
};
const res = await prepareParams(operation, body);
@@ -208,7 +208,7 @@ describe('#prepareParams', () => {
it('should handle a multipart body when a property is a file stream', async () => {
const operation = fileUploads.operation('/anything/multipart-formdata', 'post');
const body = {
- documentFile: fs.createReadStream('./test/__fixtures__/owlbert.png'),
+ documentFile: fs.createReadStream(require.resolve('@api/test-utils/fixtures/owlbert.png')),
};
const res = await prepareParams(operation, body);
@@ -236,12 +236,12 @@ describe('#prepareParams', () => {
it("should not reject files that don't exist", async () => {
const operation = fileUploads.operation('/anything/multipart-formdata', 'post');
const body = {
- documentFile: './test/__fixtures__/owlbert.jpg',
+ documentFile: './owlbert.doesntexist.jpg',
};
await expect(prepareParams(operation, body)).resolves.toStrictEqual({
body: {
- documentFile: './test/__fixtures__/owlbert.jpg',
+ documentFile: './owlbert.doesntexist.jpg',
},
});
});
@@ -381,7 +381,7 @@ describe('#prepareParams', () => {
describe('quirks', () => {
it('should not send special headers in body payloads', async () => {
- const basiq = await import('../__fixtures__/definitions/basiq.json').then(Oas.init);
+ const basiq = await import('@api/test-utils/definitions/basiq.json').then(Oas.init);
await basiq.dereference();
const operation = basiq.operation('/token', 'post');
@@ -400,7 +400,7 @@ describe('#prepareParams', () => {
});
it('should not duplicate a supplied header parameter if that header casing matches the spec', async () => {
- const basiq = await import('../__fixtures__/definitions/basiq.json').then(Oas.init);
+ const basiq = await import('@api/test-utils/definitions/basiq.json').then(Oas.init);
await basiq.dereference();
const operation = basiq.operation('/token', 'post');
@@ -482,7 +482,7 @@ describe('#prepareParams', () => {
describe('defaults', () => {
it('should prefill defaults for required body parameters if not supplied', async () => {
- const oas = await loadSpec('../__fixtures__/definitions/nested-defaults.json').then(Oas.init);
+ const oas = await loadSpec('@api/test-utils/definitions/nested-defaults.json').then(Oas.init);
await oas.dereference();
const operation = oas.operation('/pet', 'post');
diff --git a/packages/core/test/tsconfig.json b/packages/core/test/tsconfig.json
new file mode 100644
index 00000000..5752383d
--- /dev/null
+++ b/packages/core/test/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "noImplicitAny": false,
+ "resolveJsonModule": true
+ },
+ "include": ["../src/**/*", "*.ts", "**/*"]
+}
diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json
new file mode 100644
index 00000000..5acf6120
--- /dev/null
+++ b/packages/core/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "baseUrl": "./src",
+ "declaration": true,
+ "esModuleInterop": true,
+ "lib": ["DOM", "DOM.Iterable", "ES2020"],
+ "noImplicitAny": true,
+ "outDir": "dist/",
+ "strict": false
+ },
+ "include": ["./src/**/*"]
+}
diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts
new file mode 100644
index 00000000..8d5bc084
--- /dev/null
+++ b/packages/core/vitest.config.ts
@@ -0,0 +1,8 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ setupFiles: ['@api/test-utils/vitest.matchers.ts'],
+ },
+});
diff --git a/packages/httpsnippet-client-api/tsconfig.json b/packages/httpsnippet-client-api/tsconfig.json
index 058cffd5..b785b2bb 100644
--- a/packages/httpsnippet-client-api/tsconfig.json
+++ b/packages/httpsnippet-client-api/tsconfig.json
@@ -4,7 +4,7 @@
"baseUrl": "./src",
"declaration": true,
"esModuleInterop": true,
- "lib": ["dom", "es2020"],
+ "lib": ["DOM", "ES2020"],
"noImplicitAny": true,
"outDir": "dist/"
},
diff --git a/packages/api/test/datasets/real-world-apis.json b/packages/test-utils/datasets/real-world-apis.json
similarity index 100%
rename from packages/api/test/datasets/real-world-apis.json
rename to packages/test-utils/datasets/real-world-apis.json
diff --git a/packages/api/test/datasets/refresh-dataset b/packages/test-utils/datasets/refresh-dataset
similarity index 99%
rename from packages/api/test/datasets/refresh-dataset
rename to packages/test-utils/datasets/refresh-dataset
index d2d875b6..b082e585 100755
--- a/packages/api/test/datasets/refresh-dataset
+++ b/packages/test-utils/datasets/refresh-dataset
@@ -7,7 +7,6 @@
*/
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-param-reassign */
-/* eslint-disable vitest/require-hook */
const fs = require('fs');
fetch('https://api.apis.guru/v2/list.json')
diff --git a/packages/api/test/__fixtures__/definitions/alby.json b/packages/test-utils/definitions/alby.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/alby.json
rename to packages/test-utils/definitions/alby.json
diff --git a/packages/api/test/__fixtures__/definitions/auth-quirks.json b/packages/test-utils/definitions/auth-quirks.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/auth-quirks.json
rename to packages/test-utils/definitions/auth-quirks.json
diff --git a/packages/api/test/__fixtures__/definitions/basiq.json b/packages/test-utils/definitions/basiq.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/basiq.json
rename to packages/test-utils/definitions/basiq.json
diff --git a/packages/api/test/__fixtures__/definitions/nested-defaults.json b/packages/test-utils/definitions/nested-defaults.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/nested-defaults.json
rename to packages/test-utils/definitions/nested-defaults.json
diff --git a/packages/api/test/__fixtures__/definitions/operationid-quirks.json b/packages/test-utils/definitions/operationid-quirks.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/operationid-quirks.json
rename to packages/test-utils/definitions/operationid-quirks.json
diff --git a/packages/api/test/__fixtures__/definitions/optional-payload.json b/packages/test-utils/definitions/optional-payload.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/optional-payload.json
rename to packages/test-utils/definitions/optional-payload.json
diff --git a/packages/api/test/__fixtures__/definitions/payloads.json b/packages/test-utils/definitions/payloads.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/payloads.json
rename to packages/test-utils/definitions/payloads.json
diff --git a/packages/api/test/__fixtures__/definitions/response-title-quirks.json b/packages/test-utils/definitions/response-title-quirks.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/response-title-quirks.json
rename to packages/test-utils/definitions/response-title-quirks.json
diff --git a/packages/api/test/__fixtures__/definitions/simple.json b/packages/test-utils/definitions/simple.json
similarity index 100%
rename from packages/api/test/__fixtures__/definitions/simple.json
rename to packages/test-utils/definitions/simple.json
diff --git a/packages/api/test/helpers/fetch-mock.ts b/packages/test-utils/fetch-mock.ts
similarity index 72%
rename from packages/api/test/helpers/fetch-mock.ts
rename to packages/test-utils/fetch-mock.ts
index e43d764f..d3076fe7 100644
--- a/packages/api/test/helpers/fetch-mock.ts
+++ b/packages/test-utils/fetch-mock.ts
@@ -11,7 +11,7 @@ export const response = {
};
export const responses = {
- all: (url, opts) => {
+ all: (url: string, opts: RequestInit) => {
return {
uri: new URL(url).pathname,
headers: Object.fromEntries(opts.headers as unknown as []),
@@ -19,7 +19,7 @@ export const responses = {
};
},
- datauri: (url, opts) => {
+ datauri: (url: string, opts: RequestInit) => {
const buffer = Buffer.from(opts.body as string, 'hex');
const parser = new DatauriParser();
@@ -30,21 +30,21 @@ export const responses = {
};
},
- delay: (res, delay) => {
+ delay: (res: string | Record, delay: number) => {
return new Promise(resolve => {
setTimeout(() => resolve(res), delay);
});
},
- headers: (url, opts) => {
+ headers: (url: string, opts: RequestInit) => {
// `opts.headers` returns a `HeadersList` object instead of `Headers` as the typing suggests so
// we need to convert it to an array before converting it to an object.
return Object.fromEntries(opts.headers as unknown as []);
},
- multipart: async (url, opts) => {
- const headers = objectifyHeaders(opts.headers);
- const payload = await formDataToString(opts.body);
+ multipart: async (url: string, opts: RequestInit) => {
+ const headers = objectifyHeaders(opts.headers as unknown as []);
+ const payload = await formDataToString(opts.body as FormData);
return {
uri: new URL(url).pathname,
@@ -54,12 +54,12 @@ export const responses = {
};
},
- real: res => {
+ real: (res: string | Record) => {
return () => res;
},
requestBody: () => {
- return (url, opts) => {
+ return (url: string, opts: RequestInit) => {
return {
uri: new URL(url).pathname,
requestBody: JSON.parse(opts.body as string),
@@ -67,13 +67,13 @@ export const responses = {
};
},
- searchParams: url => {
+ searchParams: (url: string) => {
const res = new URL(url);
return `${res.pathname}${res.search}`;
},
url: (prop: keyof URL) => {
- return url => {
+ return (url: string) => {
return new URL(url)[prop];
};
},
diff --git a/packages/api/test/__fixtures__/hello.jp.txt b/packages/test-utils/fixtures/hello.jp.txt
similarity index 100%
rename from packages/api/test/__fixtures__/hello.jp.txt
rename to packages/test-utils/fixtures/hello.jp.txt
diff --git a/packages/api/test/__fixtures__/hello.txt b/packages/test-utils/fixtures/hello.txt
similarity index 100%
rename from packages/api/test/__fixtures__/hello.txt
rename to packages/test-utils/fixtures/hello.txt
diff --git a/packages/api/test/__fixtures__/owlbert-shrub.png b/packages/test-utils/fixtures/owlbert-shrub.png
similarity index 100%
rename from packages/api/test/__fixtures__/owlbert-shrub.png
rename to packages/test-utils/fixtures/owlbert-shrub.png
diff --git a/packages/api/test/__fixtures__/owlbert.png b/packages/test-utils/fixtures/owlbert.png
similarity index 100%
rename from packages/api/test/__fixtures__/owlbert.png
rename to packages/test-utils/fixtures/owlbert.png
diff --git a/packages/api/test/helpers/load-spec.ts b/packages/test-utils/load-spec.ts
similarity index 100%
rename from packages/api/test/helpers/load-spec.ts
rename to packages/test-utils/load-spec.ts
diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json
new file mode 100644
index 00000000..2f032c4d
--- /dev/null
+++ b/packages/test-utils/package.json
@@ -0,0 +1,20 @@
+{
+ "private": true,
+ "name": "@api/test-utils",
+ "version": "1.0.0",
+ "scripts": {
+ "lint:types": "tsc --noEmit"
+ },
+ "dependencies": {
+ "caseless": "^0.12.0",
+ "datauri": "^4.1.0",
+ "formdata-to-string": "^1.0.0",
+ "vitest": "^0.34.4"
+ },
+ "devDependencies": {
+ "@types/caseless": "^0.12.3",
+ "oas": "^20.10.3",
+ "typescript": "^5.2.2"
+ },
+ "prettier": "@readme/eslint-config/prettier"
+}
diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json
new file mode 100644
index 00000000..a0ae87dd
--- /dev/null
+++ b/packages/test-utils/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "baseUrl": "./src",
+ "declaration": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noImplicitAny": true
+ },
+ "include": ["./**/*"]
+}
diff --git a/packages/api/test/helpers/vitest.matchers.ts b/packages/test-utils/vitest.matchers.ts
similarity index 79%
rename from packages/api/test/helpers/vitest.matchers.ts
rename to packages/test-utils/vitest.matchers.ts
index 66c74b4b..ec6847a4 100644
--- a/packages/api/test/helpers/vitest.matchers.ts
+++ b/packages/test-utils/vitest.matchers.ts
@@ -9,11 +9,6 @@ interface CustomMatchers {
*/
toBeDereferenced(): R;
- /**
- * Assert that a Response headers object has a custom API-identifying `User Agent` header.
- */
- toHaveCustomUserAgent(): R;
-
/**
* Determine if a given `Headers` object has a given header matching a specific value.
*
@@ -31,6 +26,7 @@ interface CustomMatchers {
}
declare module 'vitest' {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
interface Assertion extends CustomMatchers {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}
@@ -51,25 +47,14 @@ expect.extend({
};
},
- toHaveCustomUserAgent(headers: string[]) {
- const userAgent = headers['user-agent'];
- const pass = userAgent.match(/^api \(node\)\/(\d+.\d+(.\d+|unit-testing))$/);
-
- if (!pass) {
- return {
- message: () => `expected a custom \`user-agent\` header to be present.\n\nreceived: ${userAgent}`,
- pass: false,
- };
- }
-
- return {
- message: () => `expected a custom \`user-agent\` header to not be present\n\nreceived: ${userAgent}`,
- pass: true,
- };
- },
-
toHaveHeader(obj: Headers, header: string, expected: RegExp | (string | number)[] | string) {
- const headers = caseless(Object.fromEntries(Array.from(obj.entries())));
+ const headers = caseless(
+ Object.fromEntries(
+ // @ts-expect-error Despite the Node typings saying otherwise `Headers.entries()` exists.
+ // https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries
+ Array.from(obj.entries()),
+ ),
+ );
// Header value should match a given regex.
if (expected instanceof RegExp) {