From d0ca3f53fd5312d2b2e1292f6ba5dd1e033e98ca Mon Sep 17 00:00:00 2001 From: Yves Lavoie Date: Fri, 24 Nov 2023 13:27:08 -0500 Subject: [PATCH] Remove axios 1.11 (#7730) * Move from axios to fetch * Allow weak etags * Force exit after all tests complete * Fix missing semicolons --- UI/jest.config.js | 2 +- UI/package.json | 2 +- UI/tests/common/jest-setup.js | 1 + UI/tests/common/jest.polyfills.js | 2 + .../API-contacts-business_types.spec.js | 145 ++++--- .../specs/openapi/API-contacts-sic.spec.js | 143 ++++--- UI/tests/specs/openapi/API-country.spec.js | 142 ++++--- UI/tests/specs/openapi/API-gifi.spec.js | 147 ++++--- UI/tests/specs/openapi/API-invoices.spec.js | 397 +++++++++--------- UI/tests/specs/openapi/API-languages.spec.js | 145 ++++--- .../openapi/API-products-pricegroups.spec.js | 141 ++++--- .../openapi/API-products-warehouses.spec.js | 148 ++++--- UI/tests/specs/views/Languages.spec.js | 7 +- UI/yarn.lock | 33 +- lib/LedgerSMB/Routes/ERP/API/Contacts.pm | 4 +- lib/LedgerSMB/Routes/ERP/API/Countries.pm | 2 +- lib/LedgerSMB/Routes/ERP/API/GeneralLedger.pm | 2 +- lib/LedgerSMB/Routes/ERP/API/Languages.pm | 2 +- lib/LedgerSMB/Routes/ERP/API/Products.pm | 4 +- 19 files changed, 826 insertions(+), 643 deletions(-) diff --git a/UI/jest.config.js b/UI/jest.config.js index 2a87ec6229..29e7a032d7 100644 --- a/UI/jest.config.js +++ b/UI/jest.config.js @@ -62,7 +62,7 @@ module.exports = { // Force Jest to exit after all tests have completed running. // This is useful when resources set up by test code cannot be adequately cleaned up. - forceExit: false, + forceExit: true, // Force coverage collection from ignored files using an array of glob patterns forceCoverageMatch: [], diff --git a/UI/package.json b/UI/package.json index 3d5c9d6a6e..2c3c81668d 100644 --- a/UI/package.json +++ b/UI/package.json @@ -58,7 +58,6 @@ "@redocly/cli": "^1.0.2", "@vue/test-utils": "2.4.1", "@vue/vue3-jest": "29.2.6", - "axios": "1.6.1", "babel-jest": "29.7.0", "babel-loader": "9.1.3", "browserslist": "4.22.1", @@ -67,6 +66,7 @@ "clean-webpack-plugin": "4.0.0", "compression-webpack-plugin": "10.0.0", "copy-webpack-plugin": "11.0.0", + "core-js": "3.33.3", "coveralls-next": "4.2.0", "css-loader": "6.8.1", "css-minimizer-webpack-plugin": "5.0.1", diff --git a/UI/tests/common/jest-setup.js b/UI/tests/common/jest-setup.js index 782c1e6bd8..f71ebe0d6b 100644 --- a/UI/tests/common/jest-setup.js +++ b/UI/tests/common/jest-setup.js @@ -1,6 +1,7 @@ /* eslint-disable no-console */ import { jest, beforeAll, afterAll, beforeEach, afterEach } from "@jest/globals"; +import 'core-js'; import { setGlobalOrigin } from 'undici'; import "./mocks/lsmb_elements"; diff --git a/UI/tests/common/jest.polyfills.js b/UI/tests/common/jest.polyfills.js index 1e7914bb83..11e2e67ea1 100644 --- a/UI/tests/common/jest.polyfills.js +++ b/UI/tests/common/jest.polyfills.js @@ -1,10 +1,12 @@ /* global globalThis */ const { TextEncoder, TextDecoder } = require('node:util') +const { performance } = require('node:perf_hooks') Object.defineProperties(globalThis, { TextDecoder: { value: TextDecoder }, TextEncoder: { value: TextEncoder }, + performance: { value: performance } }) const { Blob } = require('node:buffer') diff --git a/UI/tests/specs/openapi/API-contacts-business_types.spec.js b/UI/tests/specs/openapi/API-contacts-business_types.spec.js index 6a97883c0d..774ebb42a6 100644 --- a/UI/tests/specs/openapi/API-contacts-business_types.spec.js +++ b/UI/tests/specs/openapi/API-contacts-business_types.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database } from "./database"; @@ -35,7 +34,6 @@ let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); // Establish API mocking before all tests. @@ -48,16 +46,30 @@ afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -65,16 +77,18 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); + // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -84,7 +98,7 @@ afterEach(async () => { describe("Retrieving all Business Types", () => { it("GET /contacts/business-types should satisfy OpenAPI spec", async () => { // Get an HTTP response from your serverUrl - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/contacts/business-types", { headers: headers @@ -93,56 +107,53 @@ describe("Retrieving all Business Types", () => { expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); }); }); describe("Retrieving all Business Types with old syntax should fail", () => { it("GET /contacts/business-types/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/contacts/business-types/", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.BAD_REQUEST - ); + const res = await fetch(serverUrl + "/" + api + "/contacts/business-types/", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Retrieve non-existant Business Type", () => { it("GET /contacts/business-types/1 should not retrieve our Business Types", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/contacts/business-types/1", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_FOUND - ); + const res = await fetch(serverUrl + "/" + api + "/contacts/business-types/1", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); describe("Adding the IT Business Types", () => { it("POST /contacts/business-types/1 should allow adding Business Type", async () => { - let res = await axios.post( + let res = await fetch( serverUrl + "/" + api + "/contacts/business-types", { - description: "Big customer", - discount: 0.05 - }, - { - headers: headers + method: "POST", + body: JSON.stringify({ + description: "Big customer", + discount: "0.05" + }), + headers: { ...headers, "Content-Type": "application/json" } } ); expect(res.status).toEqual(StatusCodes.CREATED); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res.data).toSatisfySchemaInApiSpec("NewBusinessType"); }); }); describe("Validate against the default Business Type", () => { it("GET /contacts/business-types/1 should validate the new Business Type", async () => { - let res = await axios.get(serverUrl + "/" + api + "/contacts/business-types/1", { + let res = await fetch(serverUrl + "/" + api + "/contacts/business-types/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); @@ -151,34 +162,42 @@ describe("Validate against the default Business Type", () => { const businessTypeExample = API_yaml.components.examples.validBusinessType.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(businessTypeExample); }); }); describe("Modifying the new Business Type", () => { it("PUT /contacts/business-types/1 should allow updating Business Type", async () => { - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/contacts/business-types/1", { headers: headers } ); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/contacts/business-types/1", { - id: 1, - description: "Bigger customer", - discount: "0.1" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PUT", + body: JSON.stringify({ + id: 1, + description: "Bigger customer", + discount: "0.1" + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -190,24 +209,32 @@ describe("Modifying the new Business Type", () => { * Not implemented yet describe("Updating the new Business Type", () => { it("PATCH /contacts/business-types/1 should allow updating IT Business Types", async () => { - let res = await axios.get(serverUrl + "/" + api + "/contacts/business-types/1", { + let res = await fetch(serverUrl + "/" + api + "/contacts/business-types/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/contacts/business-types/1", { - id: "1", - description: "Navaho" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PATCH", + body: JSON.stringify({ + id: 1, + description: "Bigger customer", + discount: "0.1" + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + }, } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -218,21 +245,21 @@ describe("Updating the new Business Type", () => { describe("Not removing the new IT Business Types", () => { it("DELETE /contacts/business-types/1 should not allow deleting Business Type", async () => { - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/contacts/business-types/1", { headers: headers } ); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/contacts/business-types/1", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.FORBIDDEN + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( + serverUrl + "/" + api + "/contacts/business-types/1", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + } ); + expect(res.status).toEqual(StatusCodes.FORBIDDEN); }); }); diff --git a/UI/tests/specs/openapi/API-contacts-sic.spec.js b/UI/tests/specs/openapi/API-contacts-sic.spec.js index c30d3db7dc..f44ecd2738 100644 --- a/UI/tests/specs/openapi/API-contacts-sic.spec.js +++ b/UI/tests/specs/openapi/API-contacts-sic.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database } from "./database"; @@ -35,7 +34,6 @@ let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); // Establish API mocking before all tests. @@ -48,16 +46,30 @@ afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -65,16 +77,18 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); + // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -84,62 +98,59 @@ afterEach(async () => { describe("Retrieving all SIC", () => { it("GET /contacts/sic should satisfy OpenAPI spec", async () => { // Get an HTTP response from your serverUrl - let res = await axios.get(serverUrl + "/" + api + "/contacts/sic", { + let res = await fetch(serverUrl + "/" + api + "/contacts/sic", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); }); }); describe("Retrieving all SIC with old syntax should fail", () => { it("GET /contacts/sic/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/contacts/sic/", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.BAD_REQUEST - ); + let res = await fetch(serverUrl + "/" + api + "/contacts/sic/", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Retrieve non-existant SIC", () => { it("GET /contacts/sic/541510 should not retrieve our SIC", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/contacts/sic/541510", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_FOUND - ); + let res = await fetch(serverUrl + "/" + api + "/contacts/sic/541510", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); describe("Adding the IT SIC", () => { it("POST /contacts/sic/541510 should allow adding IT SIC", async () => { - let res = await axios.post( + let res = await fetch( serverUrl + "/" + api + "/contacts/sic", { - code: "541510", - description: "Design of computer systems" - }, - { - headers: headers + method: "POST", + body: JSON.stringify({ + code: "541510", + description: "Design of computer systems" + }), + headers: {...headers, "Content-Type": "application/json" } } ); expect(res.status).toEqual(StatusCodes.CREATED); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res.data).toSatisfySchemaInApiSpec("SIC"); }); }); describe("Validate against the example SIC", () => { it("GET /contacts/sic/541510 should validate IT SIC", async () => { - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/contacts/sic/541510", { headers: headers @@ -151,30 +162,38 @@ describe("Validate against the example SIC", () => { const sicExample = API_yaml.components.examples.validSIC.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(sicExample); }); }); describe("Modifying the new IT SIC", () => { it("PUT /contacts/sic/541510 should allow updating IT SIC", async () => { - let res = await axios.get(serverUrl + "/" + api + "/contacts/sic/541510", { + let res = await fetch(serverUrl + "/" + api + "/contacts/sic/541510", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/contacts/sic/541510", { - code: "541510", - description: "Design of computer systems and related services" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PUT", + body: JSON.stringify({ + code: "541510", + description: "Design of computer systems and related services" + }), + headers: { + ...headers, + "content-type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -186,24 +205,31 @@ describe("Modifying the new IT SIC", () => { * Not implemented yet describe("Updating the new IT SIC", () => { it("PATCH /contacts/sic/541510 should allow updating IT SIC", async () => { - let res = await axios.get(serverUrl + "/" + api + "/contacts/sic/541510", { + let res = await fetch(serverUrl + "/" + api + "/contacts/sic/541510", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/contacts/sic/541510", { - code: "541510", - description: "Navaho" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PATCH", + body: JSON.stringify({ + code: "541510", + description: "Navaho" + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -214,18 +240,19 @@ describe("Updating the new IT SIC", () => { describe("Not removing the new IT SIC", () => { it("DELETE /contacts/sic/541510 should allow deleting IT SIC", async () => { - let res = await axios.get(serverUrl + "/" + api + "/contacts/sic/541510", { + let res = await fetch(serverUrl + "/" + api + "/contacts/sic/541510", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/contacts/sic/541510", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.FORBIDDEN + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + + res = await fetch( + serverUrl + "/" + api + "/contacts/sic/541510", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + } ); + expect(res.status).toEqual(StatusCodes.FORBIDDEN); }); }); diff --git a/UI/tests/specs/openapi/API-country.spec.js b/UI/tests/specs/openapi/API-country.spec.js index ab6a7227c4..0448d8f67a 100644 --- a/UI/tests/specs/openapi/API-country.spec.js +++ b/UI/tests/specs/openapi/API-country.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database } from "./database"; @@ -37,7 +36,6 @@ let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); // Establish API mocking before all tests. @@ -50,16 +48,30 @@ afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -67,16 +79,18 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); + // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -86,31 +100,29 @@ afterEach(async () => { describe("Retrieving all countries", () => { it("GET /countries should satisfy OpenAPI spec", async () => { // Get an HTTP response from your serverUrl - let res = await axios.get(serverUrl + "/" + api + "/countries", { + let res = await fetch(serverUrl + "/" + api + "/countries", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); }); }); describe("Retrieving all countries with old syntax should fail", () => { it("GET /countries/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/countries/", { + let res = await fetch(serverUrl + "/" + api + "/countries/", { headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.BAD_REQUEST - ); + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Validate against the example country", () => { it("GET /countries/NL", async () => { - let res = await axios.get(serverUrl + "/" + api + "/countries/NL", { + let res = await fetch(serverUrl + "/" + api + "/countries/NL", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); @@ -119,61 +131,62 @@ describe("Validate against the example country", () => { const countryExample = API_yaml.components.examples.validCountry.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(countryExample); }); }); describe("Retrieve non-existant COUNTRY ZZ should fail", () => { it("GET /countries/ZZ should not retrieve invalid COUNTRY", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/countries/ZZ", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_FOUND - ); + let res = await fetch(serverUrl + "/" + api + "/countries/ZZ", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); describe("Adding the new Test COUNTRY ZZ", () => { it("POST /countries/ZZ should allow adding a new COUNTRY", async () => { - let res = await axios.post( - serverUrl + "/" + api + "/countries", - { + let res = await fetch(serverUrl + "/" + api + "/countries", { + method: "POST", + body: JSON.stringify({ code: "ZZ", name: "Atlantika" - }, - { - headers: headers - } - ); + }), + headers: { ...headers, "Content-Type": "application/json" } + }); expect(res.status).toEqual(StatusCodes.CREATED); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res.data).toSatisfySchemaInApiSpec("Country"); }); }); describe("Modifying the new COUNTRY ZZ", () => { it("PUT /countries/ZZ should allow modifying Atlantica", async () => { - let res = await axios.get(serverUrl + "/" + api + "/countries/ZZ", { + let res = await fetch(serverUrl + "/" + api + "/countries/ZZ", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( - serverUrl + "/" + api + "/countries/ZZ", - { + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch(serverUrl + "/" + api + "/countries/ZZ", { + method: "PUT", + body: JSON.stringify({ code: "ZZ", name: "Atlantica" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag } - ); + }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -182,27 +195,31 @@ describe("Modifying the new COUNTRY ZZ", () => { }); /* -Not implemented yet + * Not implemented yet describe("Updating the new COUNTRY ZZ", () => { it("PATCH /countries/ZZ should allow updating Atlantica", async () => { - let res = await axios.get(serverUrl + "/" + api + "/countries/ZZ", { + let res = await fetch(serverUrl + "/" + api + "/countries/ZZ", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( - serverUrl + "/" + api + "/countries/ZZ", - { + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch(serverUrl + "/" + api + "/countries/ZZ", { + method: "PATCH", + body: JSON.stringify({ code: "ZZ", name: "Atlantika" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag } - ); + }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -213,18 +230,17 @@ describe("Updating the new COUNTRY ZZ", () => { describe("Not Removing COUNTRY ZZ", () => { it("DELETE /countries/ZZ should not allow deleting Test COUNTRY", async () => { - let res = await axios.get(serverUrl + "/" + api + "/countries/ZZ", { + let res = await fetch(serverUrl + "/" + api + "/countries/ZZ", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/countries/ZZ", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.FORBIDDEN - ); + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + + res = await fetch(serverUrl + "/" + api + "/countries/ZZ", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + }); + expect(res.status).toEqual(StatusCodes.FORBIDDEN); }); }); diff --git a/UI/tests/specs/openapi/API-gifi.spec.js b/UI/tests/specs/openapi/API-gifi.spec.js index 52847733c7..6ab2c5c11d 100644 --- a/UI/tests/specs/openapi/API-gifi.spec.js +++ b/UI/tests/specs/openapi/API-gifi.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database } from "./database"; @@ -37,7 +36,6 @@ let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); // Establish API mocking before all tests. @@ -46,21 +44,34 @@ beforeAll(() => { }); }); - afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -68,16 +79,17 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -87,62 +99,56 @@ afterEach(async () => { describe("Retrieving all gifis", () => { it("GET /gifi should satisfy OpenAPI spec", async () => { // Get an HTTP response from your serverUrl - let res = await axios.get(serverUrl + "/" + api + "/gl/gifi", { + let res = await fetch(serverUrl + "/" + api + "/gl/gifi", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); }); }); describe("Retrieving all gifis with old syntax should fail", () => { it("GET /gifi/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/gl/gifi/", { + let res = await fetch(serverUrl + "/" + api + "/gl/gifi/", { headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.BAD_REQUEST - ); + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Retrieve non-existant GIFI 99999", () => { it("GET /gifi/99999 should not retrieve invalid GIFI", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/gl/gifi/99999", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_FOUND - ); + let res = await fetch(serverUrl + "/" + api + "/gl/gifi/99999", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); describe("Adding the new Test GIFI", () => { it("POST /gifi/99999 should allow adding a new GIFI", async () => { - let res = await axios.post( - serverUrl + "/" + api + "/gl/gifi", - { + let res = await fetch(serverUrl + "/" + api + "/gl/gifi", { + method: "POST", + body: JSON.stringify({ accno: "99999", description: "Test GIFI" - }, - { - headers: headers - } - ); + }), + headers: { ...headers, "Content-Type": "application/json" } + }); expect(res.status).toEqual(StatusCodes.CREATED); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res.data).toSatisfySchemaInApiSpec("GIFI"); }); }); describe("Validate against the example GIFI 99999", () => { it("GET /gifi/99999 should validate our new GIFI", async () => { - let res = await axios.get(serverUrl + "/" + api + "/gl/gifi/99999", { + let res = await fetch(serverUrl + "/" + api + "/gl/gifi/99999", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); @@ -151,30 +157,36 @@ describe("Validate against the example GIFI 99999", () => { const gifiExample = API_yaml.components.examples.validGIFI.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(gifiExample); }); }); describe("Modifying the new GIFI 99999", () => { it("PUT /gifi/99999 should allow updating Test GIFI", async () => { - let res = await axios.get(serverUrl + "/" + api + "/gl/gifi/99999", { + let res = await fetch(serverUrl + "/" + api + "/gl/gifi/99999", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( - serverUrl + "/" + api + "/gl/gifi/99999", - { - accno: "99999", - description: "Test GIFI" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } - } - ); + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( + serverUrl + "/" + api + "/gl/gifi/99999",{ + method: "PUT", + body: JSON.stringify({ + accno: "99999", + description: "Test GIFI" + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + } + }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -186,24 +198,31 @@ describe("Modifying the new GIFI 99999", () => { * Not implemented yet describe("Updating the new GIFI 99999", () => { it("PATCH /gifi/99999 should allow updating Test GIFI", async () => { - let res = await axios.get(serverUrl + "/" + api + "/gl/gifi/99999", { + let res = await fetch(serverUrl + "/" + api + "/gl/gifi/99999", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/gl/gifi/99999", { - accno: "99999", - description: "Test GIFI" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PATCH", + body: JSON.stringify({ + accno: "99999", + description: "Test GIFI" + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -214,18 +233,18 @@ describe("Updating the new GIFI 99999", () => { describe("Not Removing the new GIFI 99999", () => { it("DELETE /gifi/99999 should allow deleting Test GIFI", async () => { - let res = await axios.get(serverUrl + "/" + api + "/gl/gifi/99999", { + let res = await fetch(serverUrl + "/" + api + "/gl/gifi/99999", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/gl/gifi/99999", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.FORBIDDEN + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + + res = await fetch(serverUrl + "/" + api + "/gl/gifi/99999", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + } ); + expect(res.status).toEqual(StatusCodes.FORBIDDEN); }); }); diff --git a/UI/tests/specs/openapi/API-invoices.spec.js b/UI/tests/specs/openapi/API-invoices.spec.js index b74d588255..9ca6ee9785 100644 --- a/UI/tests/specs/openapi/API-invoices.spec.js +++ b/UI/tests/specs/openapi/API-invoices.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database, load_coa, initialize } from "./database"; @@ -25,6 +24,7 @@ const api = "erp/api/v0"; // Access to the database test user const id = Math.random().toString(36).substr(2, 6); + const username = `Jest${id}`; const password = "Tester"; const company = `lsmb_test_api_${id}`; @@ -34,7 +34,6 @@ let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); load_coa(username, password, company, "locale/coa/us/General.xml"); initialize(company,"UI/tests/specs/data/Invoices.sql"); @@ -49,16 +48,30 @@ afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -66,16 +79,17 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -84,13 +98,10 @@ afterEach(async () => { // Invoice tests describe("Retrieving all invoices", () => { it("GET /invoices fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/invoices", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_IMPLEMENTED - ); + let res = await fetch(serverUrl + "/" + api + "/invoices", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_IMPLEMENTED); }); }); @@ -98,80 +109,81 @@ describe("Adding the new Invoice", () => { it("POST /invoices should allow adding a new invoice", async () => { let res; try { - res = await axios.post( + res = await fetch( serverUrl + "/" + api + "/invoices", { - eca: { - number: "Customer 1", - type: "customer" // Watch for exact case or watch serverUrl stack dump - }, - account: { - accno: "1200" - }, - currency: "USD", - dates: { - created: "2022-09-01", - due: "2022-10-01", - book: "2022-10-05" - }, - "internal-notes": "Internal notes", - "invoice-number": "2389434", - lines: [ - { - part: { - number: "p1" - }, - price: 56.78, - price_fixated: false, - unit: "lbs", - qty: 1, - taxform: true, - serialnumber: "1234567890", - discount: 12, - discount_type: "%", - delivery_date: "2022-10-27", - description: "A description" - } - ], - notes: "Notes", - "order-number": "order 345", - "po-number": "po 456", - "shipping-point": "shipping from here", - // TODO: Debug ship-to - // "ship-to": "ship to there", - "ship-via": "ship via", - taxes: { - "2150": { - tax: { - category: "2150" - }, - "base-amount": 50, - amount: 6.78, - source: "Part 1", - memo: "tax memo" // Could that be optional? + method: "POST", + body: JSON.stringify({ + eca: { + number: "Customer 1", + type: "customer" // Watch for exact case or watch serverUrl stack dump }, - }, - payments: [ - { - account: { - accno: "5010" + account: { + accno: "1200" + }, + currency: "USD", + dates: { + created: "2022-09-01", + due: "2022-10-01", + book: "2022-10-05" + }, + "internal-notes": "Internal notes", + "invoice-number": "2389434", + lines: [ + { + part: { + number: "p1" + }, + price: 56.78, + price_fixated: false, + unit: "lbs", + qty: 1, + taxform: true, + serialnumber: "1234567890", + discount: 12, + discount_type: "%", + delivery_date: "2022-10-27", + description: "A description" + } + ], + notes: "Notes", + "order-number": "order 345", + "po-number": "po 456", + "shipping-point": "shipping from here", + // TODO: Debug ship-to + // "ship-to": "ship to there", + "ship-via": "ship via", + taxes: { + "2150": { + tax: { + category: "2150" + }, + "base-amount": 50, + amount: 6.78, + source: "Part 1", + memo: "tax memo" // Could that be optional? }, - date: "2022-11-05", - amount: 20, - memo: "depot", - source: "visa" - } - ] - }, - { - headers: headers + }, + payments: [ + { + account: { + accno: "5010" + }, + date: "2022-11-05", + amount: 20, + memo: "depot", + source: "visa" + } + ] + }), + headers: { ...headers, "Content-Type": "application/json" }, } - ); + ) } catch(e) { console.log(e.response.data); } expect(res.status).toEqual(StatusCodes.CREATED); - expect(res.headers.location).toMatch('./1'); + expect(res.headers.get('location')).toMatch('./1'); }); }); @@ -180,75 +192,75 @@ describe("Adding the new Invoice", () => { it("POST /invoices should allow adding a new invoice", async () => { let res; try { - res = await axios.post( + res = await fetch( serverUrl + "/" + api + "/invoices", { - eca: { - number: "Customer 1", - type: "customer" // Watch for exact case or watch serverUrl stack dump - }, - account: { - accno: "1200" - }, - currency: "USD", - dates: { - created: "2022-09-01", - due: "2022-10-01", - book: "2022-10-05" - }, - description: "Annual gizmos", - lines: [ - { - part: { - number: "p1" - }, - price: 56.78, - price_fixated: false, - unit: "lbs", - qty: 1, - taxform: true, - serialnumber: "1234567890", - discount: 12, - discount_type: "%", - delivery_date: "2022-10-27", - description: "A description" - } - ], - notes: "Notes", - "internal-notes": "Internal notes", - "invoice-number": "2389434", - "order-number": "order 345", - "po-number": "po 456", - "ship-via": "ship via", - "shipping-point": "shipping from here", - "ship-to": "ship to there", - taxes: { - "2150": { - tax: { - category: "2150" - }, - "base-amount": 50, - amount: 6.78, - source: "Part 1", - memo: "tax memo" // Could that be optional? + method: "POST", + body: JSON.stringify({ + eca: { + number: "Customer 1", + type: "customer" // Watch for exact case or watch serverUrl stack dump }, - }, - payments: [ - { - account: { - accno: "5010" + account: { + accno: "1200" + }, + currency: "USD", + dates: { + created: "2022-09-01", + due: "2022-10-01", + book: "2022-10-05" + }, + "internal-notes": "Internal notes", + "invoice-number": "2389434", + lines: [ + { + part: { + number: "p1" + }, + price: 56.78, + price_fixated: false, + unit: "lbs", + qty: 1, + taxform: true, + serialnumber: "1234567890", + discount: 12, + discount_type: "%", + delivery_date: "2022-10-27", + description: "A description" + } + ], + notes: "Notes", + "order-number": "order 345", + "po-number": "po 456", + "shipping-point": "shipping from here", + // TODO: Debug ship-to + // "ship-to": "ship to there", + "ship-via": "ship via", + taxes: { + "2150": { + tax: { + category: "2150" + }, + "base-amount": 50, + amount: 6.78, + source: "Part 1", + memo: "tax memo" // Could that be optional? }, - date: "2022-11-05", - amount: 20, - memo: "depot", - source: "visa" - } - ] - }, - { - headers: headers - } - ); + }, + payments: [ + { + account: { + accno: "5010" + }, + date: "2022-11-05", + amount: 20, + memo: "depot", + source: "visa" + } + ] + }), + headers: { ...headers, "Content-Type": "application/json" } + }); } catch(e) { console.log(e.response.data); } @@ -260,24 +272,22 @@ describe("Adding the new Invoice", () => { describe("Retrieving all invoices with old syntax should fail", () => { it("GET /invoices/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/invoices/", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.BAD_REQUEST - ); + const res = await fetch(serverUrl + "/" + api + "/invoices/", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Retrieve first invoice", () => { it("GET /invoices/1 should work and satisfy the OpenAPI spec", async () => { - let res = await axios.get(serverUrl + "/" + api + "/invoices/1", { + let res = await fetch(serverUrl + "/" + api + "/invoices/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -287,7 +297,7 @@ describe("Retrieve first invoice", () => { describe("Validate first invoice against example", () => { it("GET /invoices/1 should validate against example", async () => { - let res = await axios.get(serverUrl + "/" + api + "/invoices/1", { + let res = await fetch(serverUrl + "/" + api + "/invoices/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); @@ -296,33 +306,34 @@ describe("Validate first invoice against example", () => { const invoiceExample = API_yaml.components.examples.validInvoice.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(invoiceExample); }); }); describe("Retrieve non-existant Invoice", () => { it("GET /invoices/2 should not retrieve Invoice 2", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/invoices/2", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_FOUND - ); + let res = await fetch(serverUrl + "/" + api + "/invoices/2", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); /* -describe("Modifying the Invoice 1", () => { +describe("Modifying the new Invoice", () => { it("PUT /invoices/1 should allow updating Invoice 1", async () => { - let res = await axios.get(serverUrl + "/" + api + "/invoices/1", { + let res = await fetch(serverUrl + "/" + api + "/invoices/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/invoices/1", { + method: "PUT", + body: JSON.stringify({ eca: { number: "Customer 1", type: "customer" // Watch for exact case or watch serverUrl stack dump @@ -383,14 +394,18 @@ describe("Modifying the Invoice 1", () => { source: "Amex" } ] - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -402,23 +417,30 @@ describe("Modifying the Invoice 1", () => { * Not implemented yet describe("Updating the Invoice 1", () => { it("PATCH /invoices/1 should allow updating Invoice 1", async () => { - let res = await axios.get(serverUrl + "/" + api + "/invoices/1", { + let res = await fetch(serverUrl + "/" + api + "/invoices/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/invoices/1", { - ... - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PATCH", + body: JSON.stringify({ + // ... + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -428,19 +450,18 @@ describe("Updating the Invoice 1", () => { describe("Not removing Invoice 1", () => { it("DELETE /invoices/1 should allow deleting Invoice 1", async () => { - let res = await axios.get(serverUrl + "/" + api + "/invoices/1", { + let res = await fetch(serverUrl + "/" + api + "/invoices/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/invoices/1", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_IMPLEMENTED - ); + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + + res = await fetch(serverUrl + "/" + api + "/invoices/1", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + }); + expect(res.status).toEqual(StatusCodes.NOT_IMPLEMENTED); }); }); */ diff --git a/UI/tests/specs/openapi/API-languages.spec.js b/UI/tests/specs/openapi/API-languages.spec.js index b8b866ee03..16d5dfd99f 100644 --- a/UI/tests/specs/openapi/API-languages.spec.js +++ b/UI/tests/specs/openapi/API-languages.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database } from "./database"; @@ -24,18 +23,19 @@ const API_yaml = require (openapi + "/openapi/API.yaml"); const api = "erp/api/v0"; // Access to the database test user + const id = Math.random().toString(36).substr(2, 6); const username = `Jest${id}`; const password = "Tester"; const company = `lsmb_test_api_${id}`; + const serverUrl = process.env.LSMB_BASE_URL; let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); // Establish API mocking before all tests. @@ -48,16 +48,30 @@ afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -65,16 +79,18 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); + // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -84,36 +100,35 @@ afterEach(async () => { describe("Retrieving all languages", () => { it("GET /languages should satisfy OpenAPI spec", async () => { // Get an HTTP response from your serverUrl - let res = await axios.get(serverUrl + "/" + api + "/languages", { + let res = await fetch(serverUrl + "/" + api + "/languages", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); }); }); describe("Retrieving all languages with old syntax should fail", () => { it("GET /languages/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/languages/", { + let res = await fetch(serverUrl + "/" + api + "/languages/", { headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.BAD_REQUEST - ); + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Retrieve English language", () => { it("GET /language/en should work and satisfy the OpenAPI spec", async () => { - let res = await axios.get(serverUrl + "/" + api + "/languages/en", { + let res = await fetch(serverUrl + "/" + api + "/languages/en", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -123,7 +138,7 @@ describe("Retrieve English language", () => { describe("Retrieve the French Canadian language", () => { it("GET /language/fr_CA", async () => { - let res = await axios.get(serverUrl + "/" + api + "/languages/fr_CA", { + let res = await fetch(serverUrl + "/" + api + "/languages/fr_CA", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); @@ -132,61 +147,62 @@ describe("Retrieve the French Canadian language", () => { const languageExample = API_yaml.components.examples.validLanguage.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(languageExample); }); }); describe("Retrieve non-existant Navaho language", () => { it("GET /languages/nv should not retrieve Navajo language", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/languages/nv", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_FOUND - ); + let res = await fetch(serverUrl + "/" + api + "/languages/nv", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); describe("Adding the new Navaho Language", () => { it("POST /languages/nv should allow adding Navaho language", async () => { - let res = await axios.post( - serverUrl + "/" + api + "/languages", - { + let res = await fetch(serverUrl + "/" + api + "/languages", { + method: "POST", + body: JSON.stringify({ code: "nv", description: "Navaho" - }, - { - headers: headers - } - ); + }), + headers: { ...headers, "Content-Type": "application/json" } + }); expect(res.status).toEqual(StatusCodes.CREATED); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res.data).toSatisfySchemaInApiSpec("Language"); }); }); describe("Modifying the new Navajo language", () => { it("PUT /languages/nv should allow updating Navajo language", async () => { - let res = await axios.get(serverUrl + "/" + api + "/languages/nv", { + let res = await fetch(serverUrl + "/" + api + "/languages/nv", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( - serverUrl + "/" + api + "/languages/nv", - { + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch(serverUrl + "/" + api + "/languages/nv", { + method: "PUT", + body: JSON.stringify({ code: "nv", description: "Navajo" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag } - ); + }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -198,24 +214,28 @@ describe("Modifying the new Navajo language", () => { * Not implemented yet describe("Updating the new Navaho language", () => { it("PATCH /languages/nv should allow updating Navajo language", async () => { - let res = await axios.get(serverUrl + "/" + api + "/languages/nv", { + let res = await fetch(serverUrl + "/" + api + "/languages/nv", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( - serverUrl + "/" + api + "/languages/nv", - { + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch(serverUrl + "/" + api + "/languages/nv", { + method: "PATCH", + body: JSON.stringify({ code: "nv", description: "Navaho" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag } - ); + }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -226,18 +246,17 @@ describe("Updating the new Navaho language", () => { describe("Not removing the new Navajo language", () => { it("DELETE /languages/nv should allow deleting Navajo language", async () => { - let res = await axios.get(serverUrl + "/" + api + "/languages/nv", { + let res = await fetch(serverUrl + "/" + api + "/languages/nv", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/languages/nv", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.FORBIDDEN - ); + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + + res = await fetch(serverUrl + "/" + api + "/languages/nv", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + }); + expect(res.status).toEqual(StatusCodes.FORBIDDEN); }); }); diff --git a/UI/tests/specs/openapi/API-products-pricegroups.spec.js b/UI/tests/specs/openapi/API-products-pricegroups.spec.js index dfb8564028..dcf57f3390 100644 --- a/UI/tests/specs/openapi/API-products-pricegroups.spec.js +++ b/UI/tests/specs/openapi/API-products-pricegroups.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database } from "./database"; @@ -35,7 +34,6 @@ let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); // Establish API mocking before all tests. @@ -48,16 +46,30 @@ afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -65,16 +77,18 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); + // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -84,7 +98,7 @@ afterEach(async () => { describe("Retrieving all products/pricegroups", () => { it("GET /products/pricegroups should satisfy OpenAPI spec", async () => { // Get an HTTP response from your serverUrl - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/products/pricegroups", { headers: headers @@ -93,51 +107,57 @@ describe("Retrieving all products/pricegroups", () => { expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); }); }); describe("Retrieving all products/pricegroups with old syntax should fail", () => { it("GET /products/pricegroups/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/products/pricegroups/", { - headers: headers - }) - ).rejects.toThrow("Request failed with status code " + StatusCodes.BAD_REQUEST); + let res = await fetch(serverUrl + "/" + api + "/products/pricegroups/", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Retrieve non-existant Pricegroup1", () => { it("GET /products/pricegroups/nv should not retrieve Pricegroup1", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/products/pricegroups/1", { - headers: headers - }) - ).rejects.toThrow("Request failed with status code " + StatusCodes.NOT_FOUND); + let res = await fetch(serverUrl + "/" + api + "/products/pricegroups/1", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); describe("Adding the new Price Group", () => { it("POST /products/pricegroups/Pricegroup1 should allow adding Pricegroup1", async () => { - let res = await axios.post( + let res = await fetch( serverUrl + "/" + api + "/products/pricegroups", { - description: "Pricegroup1" - }, - { - headers: headers + method: "POST", + body: JSON.stringify({ + name: "Pricegroup1", + description: "Pricegroup1" + }), + headers: { + "X-Requested-With": "XMLHttpRequest", + "Content-Type": "application/json", + ...headers + } } ); expect(res.status).toEqual(StatusCodes.CREATED); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res.data).toSatisfySchemaInApiSpec("Pricegroup"); }); }); describe("Validate against the example Pricegroup", () => { it("GET /products/pricegroups/1", async () => { - let res = await axios.get(serverUrl + "/" + api + "/products/pricegroups/1", { + let res = await fetch(serverUrl + "/" + api + "/products/pricegroups/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); @@ -146,33 +166,41 @@ describe("Validate against the example Pricegroup", () => { const pricegroupExample = API_yaml.components.examples.validPricegroup.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(pricegroupExample); }); }); describe("Modifying the new Price Group", () => { it("PUT /products/pricegroups/Pricegroup1 should allow updating Pricegroup1", async () => { - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/products/pricegroups/1", { headers: headers } ); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/products/pricegroups/1", { - id: 1, - description: "PriceGroup1" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PUT", + body: JSON.stringify({ + id: 1, + description: "PriceGroup1" + }), + headers: { + ...headers, + "content-type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -184,23 +212,31 @@ describe("Modifying the new Price Group", () => { * Not implemented yet describe("Updating the new Pricegroup1", () => { it("PATCH /products/pricegroups/nv should allow updating Pricegroup1", async () => { - let res = await axios.get(serverUrl + "/" + api + "/products/pricegroups/PriceGroup1", { + let res = await fetch(serverUrl + "/" + api + "/products/pricegroups/PriceGroup1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/products/pricegroups/nv", { - description: "Pricegroup1" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PATCH", + body: JSON.stringify({ + description: "Pricegroup1" + }), + headers: { + "X-Requested-With": "XMLHttpRequest", + "Content-Type": "application/json", + ...headers, + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -211,21 +247,20 @@ describe("Updating the new Pricegroup1", () => { describe("Not removing the new Price Group", () => { it("DELETE /products/pricegroups/PriceGroup1 should allow deleting Pricegroup1", async () => { - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/products/pricegroups/1", { headers: headers } ); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/products/pricegroups/1", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.FORBIDDEN - ); + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + + res = await fetch(serverUrl + "/" + api + "/products/pricegroups/1", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + }); + expect(res.status).toEqual(StatusCodes.FORBIDDEN); }); }); diff --git a/UI/tests/specs/openapi/API-products-warehouses.spec.js b/UI/tests/specs/openapi/API-products-warehouses.spec.js index b6f19d1719..306a8061bd 100644 --- a/UI/tests/specs/openapi/API-products-warehouses.spec.js +++ b/UI/tests/specs/openapi/API-products-warehouses.spec.js @@ -7,7 +7,6 @@ */ // Import test packages -import axios from "axios"; import jestOpenAPI from "jest-openapi"; import { StatusCodes } from "http-status-codes"; import { create_database, drop_database } from "./database"; @@ -35,7 +34,6 @@ let headers = {}; // For all tests beforeAll(() => { - axios.defaults.adapter = 'http'; create_database(username, password, company); // Establish API mocking before all tests. @@ -48,16 +46,30 @@ afterAll(() => { drop_database(company); }); +const emulateAxiosResponse = async(res) => { + return { + data: await res.json(), + status: res.status, + statusText: res.statusText, + headers: res.headers, + request: { + path: res.url, + method: 'GET' + } + }; +}; + // Log in before each test beforeEach(async () => { - let r = await axios.post( + let r = await fetch( serverUrl + "/login.pl?action=authenticate&company=" + encodeURI(company), { - company: company, - password: password, - login: username - }, - { + method: "POST", + body: JSON.stringify({ + company: company, + password: password, + login: username + }), headers: { "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/json" @@ -65,16 +77,18 @@ beforeEach(async () => { } ); if (r.status === StatusCodes.OK) { + const data = await r.json(); headers = { - cookie: r.headers["set-cookie"], - referer: serverUrl + "/" + r.data.target, + cookie: r.headers.get("set-cookie"), + referer: serverUrl + "/" + data.target, authorization: "Basic " + btoa(username + ":" + password) }; } }); + // Log out after each test afterEach(async () => { - let r = await axios.get(serverUrl + "/login.pl?action=logout&target=_top"); + let r = await fetch(serverUrl + "/login.pl?action=logout&target=_top"); if (r.status === StatusCodes.OK) { headers = {}; } @@ -84,61 +98,62 @@ afterEach(async () => { describe("Retrieving all products/warehouses", () => { it("GET /products/warehouses should satisfy OpenAPI spec", async () => { // Get an HTTP response from your serverUrl - let res = await axios.get(serverUrl + "/" + api + "/products/warehouses", { + let res = await fetch(serverUrl + "/" + api + "/products/warehouses", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); }); }); describe("Retrieving all products/warehouses with old syntax should fail", () => { it("GET /products/warehouses/ should fail", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/products/warehouses/", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.BAD_REQUEST - ); + const res = await fetch(serverUrl + "/" + api + "/products/warehouses/", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.BAD_REQUEST); }); }); describe("Retrieve non-existant Warehouse1", () => { it("GET /products/warehouses/nv should not retrieve Warehouse1", async () => { - await expect( - axios.get(serverUrl + "/" + api + "/products/warehouses/1", { - headers: headers - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.NOT_FOUND - ); + const res = await fetch(serverUrl + "/" + api + "/products/warehouses/1", { + headers: headers + }); + expect(res.status).toEqual(StatusCodes.NOT_FOUND); }); }); describe("Adding the new Warehouse", () => { it("POST /products/warehouses/Warehouse1 should allow adding Warehouse1", async () => { - let res = await axios.post( + let res = await fetch( serverUrl + "/" + api + "/products/warehouses", { - description: "Warehouse1" - }, - { - headers: headers + method: "POST", + body: JSON.stringify({ + description: "Warehouse1" + }), + headers: { + "X-Requested-With": "XMLHttpRequest", + "Content-Type": "application/json", + ...headers + }, } ); expect(res.status).toEqual(StatusCodes.CREATED); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res.data).toSatisfySchemaInApiSpec("Warehouse"); }); }); describe("Validate against the example Warehouse", () => { it("GET /products/warehouses/1", async () => { - let res = await axios.get(serverUrl + "/" + api + "/products/warehouses/1", { + let res = await fetch(serverUrl + "/" + api + "/products/warehouses/1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); @@ -147,33 +162,41 @@ describe("Validate against the example Warehouse", () => { const warehouseExample = API_yaml.components.examples.validWarehouse.value; // Assert that the response matches the example in the spec + res = await emulateAxiosResponse(res); expect(res.data).toEqual(warehouseExample); }); }); describe("Modifying the new Warehouse", () => { it("PUT /products/warehouses/Warehouse1 should allow updating Warehouse1", async () => { - let res = await axios.get( + let res = await fetch( serverUrl + "/" + api + "/products/warehouses/1", { headers: headers } ); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.put( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/products/warehouses/1", { - id: 1, - description: "PriceGroup1" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PUT", + body: JSON.stringify({ + id: 1, + description: "PriceGroup1" + }), + headers: { + ...headers, + "content-type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -185,23 +208,30 @@ describe("Modifying the new Warehouse", () => { * Not implemented yet describe("Updating the new Warehouse1", () => { it("PATCH /products/warehouses/nv should allow updating Warehouse1", async () => { - let res = await axios.get(serverUrl + "/" + api + "/products/warehouses/PriceGroup1", { + let res = await fetch(serverUrl + "/" + api + "/products/warehouses/PriceGroup1", { headers: headers }); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - res = await axios.patch( + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + res = await fetch( serverUrl + "/" + api + "/products/warehouses/nv", { - description: "Warehouse1" - }, - { - headers: { ...headers, "If-Match": res.headers.etag } + method: "PATCH", + body: JSON.stringify({ + description: "Warehouse1" + }), + headers: { + ...headers, + "Content-Type": "application/json", + "If-Match": etag + } } ); expect(res.status).toEqual(StatusCodes.OK); // Assert that the HTTP response satisfies the OpenAPI spec + res = await emulateAxiosResponse(res); expect(res).toSatisfyApiSpec(); // Assert that the HTTP response satisfies the OpenAPI spec @@ -210,23 +240,21 @@ describe("Updating the new Warehouse1", () => { }); */ -describe("Not removing the new Price Group", () => { +describe("Not removing the new Warehouse", () => { it("DELETE /products/warehouses/PriceGroup1 should allow deleting Warehouse1", async () => { - let res = await axios.get( - serverUrl + "/" + api + "/products/warehouses/1", - { + let res = await fetch( + serverUrl + "/" + api + "/products/warehouses/1", { headers: headers } ); expect(res.status).toEqual(StatusCodes.OK); - expect(res.headers.etag).toBeDefined(); - - await expect( - axios.delete(serverUrl + "/" + api + "/products/warehouses/1", { - headers: { ...headers, "If-Match": res.headers.etag } - }) - ).rejects.toThrow( - "Request failed with status code " + StatusCodes.FORBIDDEN - ); + const etag = res.headers.get("etag"); + expect(etag).toBeDefined(); + + res = await fetch(serverUrl + "/" + api + "/products/warehouses/1", { + method: "DELETE", + headers: { ...headers, "If-Match": etag } + }); + expect(res.status).toEqual(StatusCodes.FORBIDDEN); }); }); diff --git a/UI/tests/specs/views/Languages.spec.js b/UI/tests/specs/views/Languages.spec.js index b5179abdde..4c62b37b6c 100644 --- a/UI/tests/specs/views/Languages.spec.js +++ b/UI/tests/specs/views/Languages.spec.js @@ -18,10 +18,7 @@ describe("Languages", () => { wrapper = factory(Languages); sessionUser = useSessionUserStore(); }); - afterEach(() => { - // wrapper.unmount(); - }); - + it("should show dialog", async () => { expect(wrapper.exists()).toBeTruthy(); @@ -47,7 +44,7 @@ describe("Languages", () => { ["fr", "Français"] ]); - // TODO: Test links + // TODO: Test links // expect that the links displayed match // what was returned by the API }); diff --git a/UI/yarn.lock b/UI/yarn.lock index df587ed7d5..7d9e9ea37f 100644 --- a/UI/yarn.lock +++ b/UI/yarn.lock @@ -1890,9 +1890,9 @@ integrity sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw== "@fastify/busboy@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8" - integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" + integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== "@humanwhocodes/config-array@^0.11.13": version "0.11.13" @@ -3441,15 +3441,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.1.tgz#76550d644bf0a2d469a01f9244db6753208397d7" - integrity sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - axios@^0.21.1: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" @@ -4279,6 +4270,11 @@ core-js-compat@^3.32.2: dependencies: browserslist "^4.22.1" +core-js@3.33.3: + version "3.33.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.33.3.tgz#3c644a323f0f533a0d360e9191e37f7fc059088d" + integrity sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw== + core-js@^3.32.1: version "3.32.2" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.2.tgz#172fb5949ef468f93b4be7841af6ab1f21992db7" @@ -5950,10 +5946,10 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.0.0, follow-redirects@^1.14.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== for-each@^0.3.3: version "0.3.3" @@ -9550,11 +9546,6 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" diff --git a/lib/LedgerSMB/Routes/ERP/API/Contacts.pm b/lib/LedgerSMB/Routes/ERP/API/Contacts.pm index f41b1e038d..a8998535ce 100644 --- a/lib/LedgerSMB/Routes/ERP/API/Contacts.pm +++ b/lib/LedgerSMB/Routes/ERP/API/Contacts.pm @@ -215,7 +215,7 @@ get api '/contacts/sic/{id}' => sub { put api '/contacts/sic/{id}' => sub { my ($env, $r, $c, $body, $params) = @_; - my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*"(.*)"\s*$/); + my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*(?>W\/)?"(.*)"\s*$/); my ($response, $meta) = _update_sic( $c, { code => $params->{id}, @@ -434,7 +434,7 @@ get api '/contacts/business-types/{id}' => sub { put api '/contacts/business-types/{id}' => sub { my ($env, $r, $c, $body, $params) = @_; - my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*"(.*)"\s*$/); + my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*(?>W\/)?"(.*)"\s*$/); my ($response, $meta) = _update_businesstype( $c, { id => $params->{id}, diff --git a/lib/LedgerSMB/Routes/ERP/API/Countries.pm b/lib/LedgerSMB/Routes/ERP/API/Countries.pm index a096fef5a6..8458c40a51 100644 --- a/lib/LedgerSMB/Routes/ERP/API/Countries.pm +++ b/lib/LedgerSMB/Routes/ERP/API/Countries.pm @@ -214,7 +214,7 @@ get api '/countries/{id}' => sub { put api '/countries/{id}' => sub { my ($env, $r, $c, $body, $params) = @_; - my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*"(.*)"\s*$/); + my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*(?>W\/)?"(.*)"\s*$/); my ($response, $meta) = _update_country( $c, { code => $params->{id}, diff --git a/lib/LedgerSMB/Routes/ERP/API/GeneralLedger.pm b/lib/LedgerSMB/Routes/ERP/API/GeneralLedger.pm index 5e5359db7c..bca0e239a1 100644 --- a/lib/LedgerSMB/Routes/ERP/API/GeneralLedger.pm +++ b/lib/LedgerSMB/Routes/ERP/API/GeneralLedger.pm @@ -213,7 +213,7 @@ get api '/gl/gifi/{id}' => sub { put api '/gl/gifi/{id}' => sub { my ($env, $r, $c, $body, $params) = @_; - my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*"(.*)"\s*$/); + my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*(?>W\/)?"(.*)"\s*$/); my ($response, $meta) = _update_gifi( $c, { accno => $params->{id}, diff --git a/lib/LedgerSMB/Routes/ERP/API/Languages.pm b/lib/LedgerSMB/Routes/ERP/API/Languages.pm index 220b0515ce..606d339421 100644 --- a/lib/LedgerSMB/Routes/ERP/API/Languages.pm +++ b/lib/LedgerSMB/Routes/ERP/API/Languages.pm @@ -213,7 +213,7 @@ get api '/languages/{id}' => sub { put api '/languages/{id}' => sub { my ($env, $r, $c, $body, $params) = @_; - my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*"(.*)"\s*$/); + my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*(?>W\/)?"(.*)"\s*$/); my ($response, $meta) = _update_language( $c, { code => $params->{id}, diff --git a/lib/LedgerSMB/Routes/ERP/API/Products.pm b/lib/LedgerSMB/Routes/ERP/API/Products.pm index c2402065bc..2c6e82e1e8 100644 --- a/lib/LedgerSMB/Routes/ERP/API/Products.pm +++ b/lib/LedgerSMB/Routes/ERP/API/Products.pm @@ -213,7 +213,7 @@ get api '/products/pricegroups/{id}' => sub { put api '/products/pricegroups/{id}' => sub { my ($env, $r, $c, $body, $params) = @_; - my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*"(.*)"\s*$/); + my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*(?>W\/)?"(.*)"\s*$/); my ($response, $meta) = _update_pricegroup( $c, { id => $params->{id}, @@ -426,7 +426,7 @@ get api '/products/warehouses/{id}' => sub { put api '/products/warehouses/{id}' => sub { my ($env, $r, $c, $body, $params) = @_; - my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*"(.*)"\s*$/); + my ($ETag) = ($r->headers->header('If-Match') =~ m/^\s*(?>W\/)?"(.*)"\s*$/); my ($response, $meta) = _update_warehouse( $c, { id => $params->{id},