diff --git a/sasjs-tests/package-lock.json b/sasjs-tests/package-lock.json index 3e75506..fafcc5d 100644 --- a/sasjs-tests/package-lock.json +++ b/sasjs-tests/package-lock.json @@ -1378,6 +1378,11 @@ } } }, + "@sasjs/test-framework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.0.1.tgz", + "integrity": "sha512-SA+Rc5N+r29O1OwtZPR7O/Km3FFy3X7zWSYhV+y9cTrruGvILE5mYOS1T9yJb9JJNno3DmtXC+y1XD97F+hDmQ==" + }, "@sheerun/mutationobserver-shim": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", diff --git a/sasjs-tests/package.json b/sasjs-tests/package.json index ca165e1..1f9356e 100644 --- a/sasjs-tests/package.json +++ b/sasjs-tests/package.json @@ -5,6 +5,7 @@ "private": true, "dependencies": { "@sasjs/adapter": "^1.0.5", + "@sasjs/test-framework": "^1.0.1", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", diff --git a/sasjs-tests/src/App.tsx b/sasjs-tests/src/App.tsx index 4ede90b..45055bf 100644 --- a/sasjs-tests/src/App.tsx +++ b/sasjs-tests/src/App.tsx @@ -1,16 +1,28 @@ import React, { ReactElement, useState, useContext, useEffect } from "react"; +import { TestSuiteRunner, TestSuite, AppContext } from "@sasjs/test-framework"; +import { basicTests } from "./testSuites/Basic"; +import { sendArrTests, sendObjTests } from "./testSuites/RequestData"; +import { specialCaseTests } from "./testSuites/SpecialCases"; +import { sasjsRequestTests } from "./testSuites/SasjsRequests"; +import "@sasjs/test-framework/dist/index.css"; import "./App.scss"; -import TestSuiteRunner from "./TestSuiteRunner"; -import { AppContext } from "./context/AppContext"; const App = (): ReactElement<{}> => { const [appLoc, setAppLoc] = useState(""); const [debug, setDebug] = useState(false); - const { adapter } = useContext(AppContext); + const { adapter, config } = useContext(AppContext); + const [testSuites, setTestSuites] = useState([]); useEffect(() => { if (adapter) { adapter.setDebugState(debug); + setTestSuites([ + basicTests(adapter, config.userName, config.password), + sendArrTests(adapter), + sendObjTests(adapter), + specialCaseTests(adapter), + sasjsRequestTests(adapter), + ]); } }, [debug, adapter]); @@ -50,7 +62,7 @@ const App = (): ReactElement<{}> => { /> - {adapter && } + {adapter && testSuites && } ); }; diff --git a/sasjs-tests/src/Login.tsx b/sasjs-tests/src/Login.tsx index 158f908..441f0a6 100644 --- a/sasjs-tests/src/Login.tsx +++ b/sasjs-tests/src/Login.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, useState, useCallback, useContext } from "react"; import "./Login.scss"; -import { AppContext } from "./context/AppContext"; +import { AppContext } from "@sasjs/test-framework"; import { Redirect } from "react-router-dom"; const Login = (): ReactElement<{}> => { diff --git a/sasjs-tests/src/PrivateRoute.tsx b/sasjs-tests/src/PrivateRoute.tsx index a70d282..8420955 100644 --- a/sasjs-tests/src/PrivateRoute.tsx +++ b/sasjs-tests/src/PrivateRoute.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, useContext, FunctionComponent } from "react"; import { Redirect, Route } from "react-router-dom"; -import { AppContext } from "./context/AppContext"; +import { AppContext } from "@sasjs/test-framework"; interface PrivateRouteProps { component: FunctionComponent; diff --git a/sasjs-tests/src/TestSuiteRunner.scss b/sasjs-tests/src/TestSuiteRunner.scss deleted file mode 100644 index 880b9db..0000000 --- a/sasjs-tests/src/TestSuiteRunner.scss +++ /dev/null @@ -1,19 +0,0 @@ -.button-container { - display: flex; - justify-content: center; - align-items: center; - padding: 16px; - - .loading-spinner { - margin: 0 8px; - } - - .submit-button { - padding: 10px; - min-height: 80px; - font-size: 2em; - display: flex; - justify-content: center; - align-items: center; - } -} diff --git a/sasjs-tests/src/TestSuiteRunner.tsx b/sasjs-tests/src/TestSuiteRunner.tsx deleted file mode 100644 index 449371b..0000000 --- a/sasjs-tests/src/TestSuiteRunner.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import React, { useEffect, useState, ReactElement, useContext } from "react"; -import TestSuiteComponent from "./components/TestSuite"; -import TestSuiteCard from "./components/TestSuiteCard"; -import { TestSuite, Test } from "./types"; -import { basicTests } from "./testSuites/Basic"; -import "./TestSuiteRunner.scss"; -import SASjs from "@sasjs/adapter"; -import { AppContext } from "./context/AppContext"; -import { sendArrTests, sendObjTests } from "./testSuites/RequestData"; -import { specialCaseTests } from "./testSuites/SpecialCases"; -import { sasjsRequestTests } from "./testSuites/SasjsRequests"; - -interface TestSuiteRunnerProps { - adapter: SASjs; -} -const TestSuiteRunner = ( - props: TestSuiteRunnerProps -): ReactElement => { - const { adapter } = props; - const { config } = useContext(AppContext); - const [testSuites, setTestSuites] = useState([]); - const [runTests, setRunTests] = useState(false); - const [completedTestSuites, setCompletedTestSuites] = useState< - { - name: string; - completedTests: { - test: Test; - result: boolean; - error: Error | null; - executionTime: number; - }[]; - }[] - >([]); - const [currentTestSuite, setCurrentTestSuite] = useState( - (null as unknown) as TestSuite - ); - - useEffect(() => { - if (adapter) { - setTestSuites([ - basicTests(adapter, config.userName, config.password), - sendArrTests(adapter), - sendObjTests(adapter), - specialCaseTests(adapter), - sasjsRequestTests(adapter), - ]); - setCompletedTestSuites([]); - } - }, [adapter]); - - useEffect(() => { - if (testSuites.length) { - setCurrentTestSuite(testSuites[0]); - } - }, [testSuites]); - - useEffect(() => { - if (runTests) { - setCompletedTestSuites([]); - setCurrentTestSuite(testSuites[0]); - } - }, [runTests, testSuites]); - - return ( - <> -
- -
- {completedTestSuites.map((completedTestSuite, index) => { - return ( - - ); - })} - {currentTestSuite && runTests && ( - { - const currentIndex = testSuites.indexOf(currentTestSuite); - const nextIndex = - currentIndex < testSuites.length - 1 ? currentIndex + 1 : -1; - if (nextIndex >= 0) { - setCurrentTestSuite(testSuites[nextIndex]); - } else { - setCurrentTestSuite(null); - } - const newCompletedTestSuites = [ - ...completedTestSuites, - { name, completedTests }, - ]; - setCompletedTestSuites(newCompletedTestSuites); - - if (newCompletedTestSuites.length === testSuites.length) { - setRunTests(false); - } - }} - /> - )} - - ); -}; - -export default TestSuiteRunner; diff --git a/sasjs-tests/src/components/Test.tsx b/sasjs-tests/src/components/Test.tsx deleted file mode 100644 index 6d13850..0000000 --- a/sasjs-tests/src/components/Test.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { ReactElement, useEffect, useState } from "react"; -import TestCard from "./TestCard"; -import { start } from "repl"; - -interface TestProps { - title: string; - description: string; - beforeTest?: (...args: any) => Promise; - afterTest?: (...args: any) => Promise; - test: (context: any) => Promise; - assertion: (...args: any) => boolean; - onCompleted: (payload: { - result: boolean; - error: Error | null; - executionTime: number; - }) => void; - context: any; -} - -const getStatus = (isRunning: boolean, isPassed: boolean): string => { - return isRunning ? "running" : isPassed ? "passed" : "failed"; -}; - -const Test = (props: TestProps): ReactElement => { - const { - title, - description, - test, - beforeTest, - afterTest, - assertion, - onCompleted, - context, - } = props; - const beforeTestFunction = beforeTest ? beforeTest : () => Promise.resolve(); - const afterTestFunction = afterTest ? afterTest : () => Promise.resolve(); - const [isRunning, setIsRunning] = useState(false); - const [isPassed, setIsPassed] = useState(false); - - useEffect(() => { - if (test && assertion) { - const startTime = new Date().valueOf(); - setIsRunning(true); - setIsPassed(false); - beforeTestFunction() - .then(() => test(context)) - .then((res) => { - setIsRunning(false); - setIsPassed(assertion(res, context)); - return Promise.resolve(assertion(res, context)); - }) - .then((testResult) => { - afterTestFunction(); - const endTime = new Date().valueOf(); - const executionTime = (endTime - startTime) / 1000; - onCompleted({ result: testResult, error: null, executionTime }); - }) - .catch((e) => { - setIsRunning(false); - setIsPassed(false); - console.error(e); - const endTime = new Date().valueOf(); - const executionTime = (endTime - startTime) / 1000; - onCompleted({ result: false, error: e, executionTime }); - }); - } - }, [test, assertion]); - - return ( - - ); -}; - -export default Test; diff --git a/sasjs-tests/src/components/TestCard.scss b/sasjs-tests/src/components/TestCard.scss deleted file mode 100644 index 306d078..0000000 --- a/sasjs-tests/src/components/TestCard.scss +++ /dev/null @@ -1,62 +0,0 @@ -.test { - display: inline-flex; - padding: 8px; - margin: 8px; - flex-direction: column; - border: 1px solid #ddd; - border-radius: 5px; - width: 20%; - - .title { - font-weight: bold; - color: #eee; - font-size: 1em; - } - - .description, - .execution-time { - color: #c6c0c0; - padding: 8px 0; - font-size: 0.8em; - } - - .description { - min-height: 50px; - } - - .execution-time { - color: #f9e804; - } - - .icon { - border-radius: 50%; - width: 12px; - height: 12px; - margin-right: 8px; - display: inline-block; - - &.running { - background-color: yellow; - } - - &.passed { - background-color: green; - } - - &.failed { - background-color: red; - } - } -} - -@media only screen and (max-width: 900px) { - .test { - width: 90%; - } -} - -@media only screen and (min-width: 901px) and (max-width: 1280px) { - .test { - width: 30%; - } -} diff --git a/sasjs-tests/src/components/TestCard.tsx b/sasjs-tests/src/components/TestCard.tsx deleted file mode 100644 index 404f39c..0000000 --- a/sasjs-tests/src/components/TestCard.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { ReactElement } from "react"; -import "./TestCard.scss"; - -interface TestCardProps { - title: string; - description: string; - status: string; - error: Error | null; - executionTime?: number; -} -const TestCard = (props: TestCardProps): ReactElement => { - const { title, description, status, error, executionTime } = props; - - return ( -
- {title} - {description} - - {executionTime ? executionTime.toFixed(2) + "s" : ""} - - {status === "running" && ( -
- Running... -
- )} - {status === "passed" && ( -
- Passed -
- )} - {status === "failed" && ( - <> -
- Failed -
- {!!error && {error.message}} - - )} -
- ); -}; - -export default TestCard; diff --git a/sasjs-tests/src/components/TestSuite.tsx b/sasjs-tests/src/components/TestSuite.tsx deleted file mode 100644 index 850d141..0000000 --- a/sasjs-tests/src/components/TestSuite.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React, { ReactElement, useState, useEffect } from "react"; -import "./TestSuiteCard.scss"; -import { Test } from "../types"; -import TestComponent from "./Test"; -import TestCard from "./TestCard"; - -interface TestSuiteProps { - name: string; - tests: Test[]; - beforeAll?: (...args: any) => Promise; - afterAll?: (...args: any) => Promise; - onCompleted: ( - name: string, - completedTests: { - test: Test; - result: boolean; - error: Error | null; - executionTime: number; - }[] - ) => void; -} -const TestSuite = (props: TestSuiteProps): ReactElement => { - const { name, tests, beforeAll, afterAll, onCompleted } = props; - const [context, setContext] = useState(null); - const [completedTests, setCompletedTests] = useState< - { - test: Test; - result: boolean; - error: Error | null; - executionTime: number; - }[] - >([]); - const [currentTest, setCurrentTest] = useState( - (null as unknown) as Test - ); - - useEffect(() => { - if (beforeAll) { - beforeAll().then((data) => setContext({ data })); - } - }, [beforeAll]); - - useEffect(() => { - if (tests.length) { - setCurrentTest(tests[0]); - } - setCompletedTests([]); - setContext(null); - }, [tests]); - - return (!!beforeAll && !!context) || !beforeAll ? ( -
-
{name}
- {currentTest && ( - { - const newCompleteTests = [ - ...completedTests, - { - test: currentTest, - result: completedTest.result, - error: completedTest.error, - executionTime: completedTest.executionTime, - }, - ]; - setCompletedTests(newCompleteTests); - const currentIndex = tests.indexOf(currentTest); - const nextIndex = - currentIndex < tests.length - 1 ? currentIndex + 1 : -1; - if (nextIndex >= 0) { - setCurrentTest(tests[nextIndex]); - } else { - setCurrentTest(null); - } - if (newCompleteTests.length === tests.length) { - if (afterAll) { - afterAll().then(() => onCompleted(name, newCompleteTests)); - } else { - onCompleted(name, newCompleteTests); - } - } - }} - /> - )} - {completedTests.map((completedTest, index) => { - const { test, result, error } = completedTest; - const { title, description } = test; - return ( - - ); - })} -
- ) : ( - <> - ); -}; - -export default TestSuite; diff --git a/sasjs-tests/src/components/TestSuiteCard.scss b/sasjs-tests/src/components/TestSuiteCard.scss deleted file mode 100644 index 9ce7289..0000000 --- a/sasjs-tests/src/components/TestSuiteCard.scss +++ /dev/null @@ -1,19 +0,0 @@ -.test-suite { - .test-suite-name { - font-size: 1.5em; - font-weight: bold; - color: #1f2027; - - &.passed { - color: green; - } - - &.failed { - color: red; - } - - &.running { - color: yellow; - } - } -} diff --git a/sasjs-tests/src/components/TestSuiteCard.tsx b/sasjs-tests/src/components/TestSuiteCard.tsx deleted file mode 100644 index c573e03..0000000 --- a/sasjs-tests/src/components/TestSuiteCard.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { ReactElement } from "react"; -import "./TestSuiteCard.scss"; -import { Test } from "../types"; -import TestCard from "./TestCard"; - -interface TestSuiteCardProps { - name: string; - tests: { - test: Test; - result: boolean; - error: Error | null; - executionTime: number; - }[]; -} -const TestSuiteCard = ( - props: TestSuiteCardProps -): ReactElement => { - const { name, tests } = props; - const overallStatus = tests.map((t) => t.result).reduce((x, y) => x && y); - - return ( -
-
- {name} -
- {tests.map((completedTest, index) => { - const { test, result, error, executionTime } = completedTest; - const { title, description } = test; - return ( - - ); - })} -
- ); -}; - -export default TestSuiteCard; diff --git a/sasjs-tests/src/context/AppContext.tsx b/sasjs-tests/src/context/AppContext.tsx deleted file mode 100644 index 6a20af3..0000000 --- a/sasjs-tests/src/context/AppContext.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { createContext, useState, useEffect, ReactNode } from "react"; -import SASjs from "@sasjs/adapter"; - -export const AppContext = createContext<{ - config: any; - sasJsConfig: any; - isLoggedIn: boolean; - setIsLoggedIn: (value: boolean) => void; - adapter: SASjs; -}>({ - config: null, - sasJsConfig: null, - isLoggedIn: false, - setIsLoggedIn: (null as unknown) as (value: boolean) => void, - adapter: (null as unknown) as SASjs, -}); - -export const AppProvider = (props: { children: ReactNode }) => { - const [config, setConfig] = useState<{ sasJsConfig: any }>({ - sasJsConfig: null, - }); - - const [adapter, setAdapter] = useState((null as unknown) as SASjs); - - const [isLoggedIn, setIsLoggedIn] = useState(false); - - useEffect(() => { - fetch("config.json") - .then((res) => res.json()) - .then((configJson: any) => { - setConfig(configJson); - const sasjs = new SASjs(configJson.sasJsConfig); - setAdapter(sasjs); - sasjs.checkSession().then((response) => { - setIsLoggedIn(response.isLoggedIn); - }); - }); - }, []); - - return ( - - {props.children} - - ); -}; diff --git a/sasjs-tests/src/index.tsx b/sasjs-tests/src/index.tsx index 48791c8..effc537 100644 --- a/sasjs-tests/src/index.tsx +++ b/sasjs-tests/src/index.tsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom"; import { Route, HashRouter, Switch } from "react-router-dom"; import "./index.scss"; import * as serviceWorker from "./serviceWorker"; -import { AppProvider } from "./context/AppContext"; +import { AppProvider } from "@sasjs/test-framework"; import PrivateRoute from "./PrivateRoute"; import Login from "./Login"; import App from "./App"; diff --git a/sasjs-tests/src/testSuites/Basic.ts b/sasjs-tests/src/testSuites/Basic.ts index 4dbf4fe..80d9c85 100644 --- a/sasjs-tests/src/testSuites/Basic.ts +++ b/sasjs-tests/src/testSuites/Basic.ts @@ -1,5 +1,5 @@ import SASjs, { ServerType, SASjsConfig } from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const defaultConfig: SASjsConfig = { serverUrl: window.location.origin, diff --git a/sasjs-tests/src/testSuites/RequestData.ts b/sasjs-tests/src/testSuites/RequestData.ts index f31a1b2..e00d868 100644 --- a/sasjs-tests/src/testSuites/RequestData.ts +++ b/sasjs-tests/src/testSuites/RequestData.ts @@ -1,5 +1,5 @@ import SASjs from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const stringData: any = { table1: [{ col1: "first col value" }] }; const numericData: any = { table1: [{ col1: 3.14159265 }] }; diff --git a/sasjs-tests/src/testSuites/SasjsRequests.ts b/sasjs-tests/src/testSuites/SasjsRequests.ts index c17f2d0..9a4a5e5 100644 --- a/sasjs-tests/src/testSuites/SasjsRequests.ts +++ b/sasjs-tests/src/testSuites/SasjsRequests.ts @@ -1,5 +1,5 @@ import SASjs from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const data: any = { table1: [{ col1: "first col value" }] }; diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index 6117805..0785204 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -1,5 +1,5 @@ import SASjs from "@sasjs/adapter"; -import { TestSuite } from "../types"; +import { TestSuite } from "@sasjs/test-framework"; const specialCharData: any = { table1: [ diff --git a/sasjs-tests/src/types/index.ts b/sasjs-tests/src/types/index.ts deleted file mode 100644 index 832aca1..0000000 --- a/sasjs-tests/src/types/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface Test { - title: string; - description: string; - beforeTest?: (...args: any) => Promise; - afterTest?: (...args: any) => Promise; - test: (context?: any) => Promise; - assertion: (...args: any) => boolean; -} - -export interface TestSuite { - name: string; - tests: Test[]; - beforeAll?: (...args: any) => Promise; - afterAll?: (...args: any) => Promise; -} diff --git a/sasjs-tests/src/utils/UploadFile.ts b/sasjs-tests/src/utils/UploadFile.ts deleted file mode 100644 index 5dafccd..0000000 --- a/sasjs-tests/src/utils/UploadFile.ts +++ /dev/null @@ -1,23 +0,0 @@ -export const uploadFile = (file: File, fileName: string, url: string) => { - return new Promise((resolve, reject) => { - const data = new FormData(); - data.append("file", file); - data.append("filename", fileName); - const xhr = new XMLHttpRequest(); - xhr.withCredentials = true; - xhr.addEventListener("readystatechange", function () { - if (this.readyState === 4) { - let response: any; - try { - response = JSON.parse(this.responseText); - } catch (e) { - reject(e); - } - resolve(response); - } - }); - xhr.open("POST", url); - xhr.setRequestHeader("cache-control", "no-cache"); - xhr.send(data); - }); -};