feat(test): Automate smoke tests for JS Client [DXJ-293] (#282)

This commit is contained in:
Pavel 2023-03-07 20:07:52 +04:00 committed by GitHub
parent 3fbeb0df8e
commit 10d7eaed80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 830 additions and 169 deletions

View File

@ -1,24 +1,25 @@
{
"name": "common-dev-deps",
"version": "0.1.0",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"type": "module",
"engines": {
"node": ">=10",
"pnpm": ">=3"
},
"scripts": {
"simulate-cdn": "http-server -p 8765 ./packages/client/js-client.web.standalone/dist"
},
"author": "Fluence Labs",
"license": "Apache-2.0",
"devDependencies": {
"http-server": "14.1.1",
"@types/node": "18.13.0",
"ts-node": "10.9.1",
"typescript": "4.7",
"@fluencelabs/aqua-lib": "0.6.0",
"@fluencelabs/aqua": "0.9.1-374"
}
"name": "common-dev-deps",
"version": "0.1.0",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"type": "module",
"engines": {
"node": ">=10",
"pnpm": ">=3"
},
"scripts": {
"simulate-cdn": "http-server -p 8765 ./packages/client/js-client.web.standalone/dist"
},
"author": "Fluence Labs",
"license": "Apache-2.0",
"devDependencies": {
"http-server": "14.1.1",
"puppeteer": "19.7.2",
"@types/node": "18.13.0",
"ts-node": "10.9.1",
"typescript": "4.7",
"@fluencelabs/aqua-lib": "0.6.0",
"@fluencelabs/aqua": "0.9.1-374"
}
}

View File

@ -3,11 +3,13 @@ import { Fluence } from '@fluencelabs/js-client.api';
import { kras, randomKras } from '@fluencelabs/fluence-network-environment';
import { registerHelloWorld, smokeTest } from './_aqua/smoke_test.js';
// Relay running on local machine
// const relay = {
// multiaddr: '/ip4/127.0.0.1/tcp/4310/ws/p2p/12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
// peerId: '12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
// };
// Currently the tests executes some calls to registry. And they fail for a single local node setup. So we use kras instead.
const relay = randomKras();
function generateRandomUint8Array() {
@ -27,7 +29,9 @@ const optsWithRandomKeyPair = () => {
} as const;
};
export const main = async () => {
export type TestResult = { res: string | null; errors: string[]; hello: string };
export const runTest = async (): Promise<TestResult> => {
try {
Fluence.onConnectionStateChange((state) => console.info('connection state changed: ', state));
@ -58,6 +62,8 @@ export const main = async () => {
} else {
console.log('aqua finished, result', res);
}
return { res, errors, hello };
} finally {
console.log('disconnecting from Fluence Network...');
await Fluence.disconnect();
@ -66,7 +72,7 @@ export const main = async () => {
};
export const runMain = () => {
main()
runTest()
.then(() => console.log('done!'))
.catch((err) => console.error('error: ', err));
};

View File

@ -1,26 +0,0 @@
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

View File

@ -1,26 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}

View File

@ -11,7 +11,7 @@
"type": "module",
"scripts": {
"build": "tsc",
"_test": "node --loader ts-node/esm ./src/index.ts"
"test": "node --loader ts-node/esm ./src/index.ts"
},
"repository": "https://github.com/fluencelabs/fluence-js",
"author": "Fluence Labs",

View File

@ -0,0 +1,4 @@
import '@fluencelabs/js-client.node';
import { runTest } from '@test/aqua_for_test';
runTest().then(() => console.log('Smoke tests succeed!'));

View File

@ -0,0 +1,7 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist"
},
"exclude": ["node_modules", "dist"]
}

View File

@ -2,6 +2,7 @@
"name": "cra-ts",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@fluencelabs/js-client.api": "workspace:^",
"@test/aqua_for_test": "workspace:^",
@ -18,8 +19,11 @@
"typescript": "4.9.5",
"web-vitals": "2.1.4"
},
"devDependencies": {},
"devDependencies": {
"@test/test-utils": "workspace:^"
},
"scripts": {
"commented_out_test": "node --loader ts-node/esm ./test/index.ts",
"start": "react-scripts start",
"build": "react-scripts build",
"_test": "react-scripts test",

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<script src='http://localhost:8765/js-client.min.js' async></script>
<script src='http://localhost:8766/js-client.min.js' async></script>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,48 @@
import { runTest } from '@test/aqua_for_test';
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
const [result, setResult] = React.useState<string | null>(null);
const [error, setError] = React.useState<string | null>(null);
const onButtonClick = () => {
runTest()
.then((res) => {
if (res.errors.length === 0) {
setResult(JSON.stringify(res));
setError(null);
} else {
setResult(null);
setError(res.errors.toString());
}
})
.catch((err) => {
setResult('');
setError(err.toString());
});
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<button id="btn" onClick={onButtonClick}>
Click to run test
</button>
{result && <div id="res">{result}</div>}
{error && <div id="error">{error}</div>}
<a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">
Learn React
</a>
</header>
</div>
);
}
export default App;

View File

@ -3,7 +3,6 @@ import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { runMain } from '@test/aqua_for_test';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
@ -16,5 +15,3 @@ root.render(
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
runMain();

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,45 @@
import puppeteer from 'puppeteer';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { startCdn, startContentServer, stopServer } from '@test/test-utils';
const port = 3001;
const uri = `http://localhost:${port}/`;
const __dirname = dirname(fileURLToPath(import.meta.url));
const publicPath = join(__dirname, '../build/');
const test = async () => {
const cdn = await startCdn(8766);
const localServer = await startContentServer(port, publicPath);
console.log('starting puppeteer...');
const browser = await puppeteer.launch();
const page = await browser.newPage();
// uncomment to debug what's happening inside the browser
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
console.log('going to the page in browser...');
await page.goto(uri);
console.log('clicking button...');
await page.click('#btn');
console.log('waiting for result to appear...');
const elem = await page.waitForSelector('#res');
console.log('getting the content of result div...');
const content = await elem?.evaluate((x) => x.textContent);
console.log('raw result: ', content);
await browser.close();
await stopServer(cdn);
await stopServer(localServer);
if (!content) {
throw new Error('smoke test failed!');
}
};
test().then(() => console.log('smoke tests succeed!'));

View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src", "test"]
}

View File

@ -8,17 +8,18 @@
"node": ">=10",
"pnpm": ">=3"
},
"type": "module",
"scripts": {
"build": "pnpm copy-script",
"serve": "http-server public",
"copy-script": "cp ../../client/js-client.web.standalone/dist/js-client.min.js ./public"
"build": "tsc",
"commented_out_test": "node --loader ts-node/esm ./src/index.ts",
"serve": "http-server public"
},
"repository": "https://github.com/fluencelabs/fluence-js",
"author": "Fluence Labs",
"license": "Apache-2.0",
"dependencies": {},
"devDependencies": {
"dependencies": {
"@fluencelabs/js-client.web.standalone": "workspace:^",
"http-server": "14.1.1"
}
"@test/test-utils": "workspace:^"
},
"devDependencies": {}
}

View File

@ -6,14 +6,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Smoke test for web</title>
<script src="./js-client.min.js"></script>
<script src="./index.js"></script>
</head>
<body>
<main>
<h1>Open console f12</h1>
<button id="btn">Click to run test</button>
<div id="res-placeholder"></div>
<script src='http://localhost:8765/js-client.min.js'></script>
<script src="./index.js"></script>
</main>
</body>

View File

@ -1,11 +1,19 @@
const peer = globalThis.defaultPeer;
const fluence = globalThis.fluence;
// Relay from kras network
// Currently the tests executes some calls to registry. And they fail for a single local node setup. So we use kras instead.
const relay = {
multiaddr: '/ip4/127.0.0.1/tcp/4310/ws/p2p/12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
peerId: '12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
multiaddr: '/dns4/kras-01.fluence.dev/tcp/19001/wss/p2p/12D3KooWKnEqMfYo9zvfHmqTLpLdiHXPe4SVqUWcWHDJdFGrSmcA',
peerId: '12D3KooWKnEqMfYo9zvfHmqTLpLdiHXPe4SVqUWcWHDJdFGrSmcA',
};
const getRelayTime = (relayPeerId) => {
// Relay running on local machine
//const relay = {
// multiaddr: '/ip4/127.0.0.1/tcp/4310/ws/p2p/12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
// peerId: '12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
//};
const getRelayTime = () => {
const script = `
(xor
(seq
@ -34,8 +42,7 @@ const getRelayTime = (relayPeerId) => {
)
)
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
)
`;
)`;
const def = {
functionName: 'getRelayTime',
@ -73,31 +80,39 @@ const getRelayTime = (relayPeerId) => {
const config = {};
const args = {};
return peer.compilerSupport.callFunction({
const args = { relayPeerId: relay.peerId };
return fluence.callAquaFunction({
args,
def,
config,
script,
config,
peer: fluence.defaultClient,
});
};
const main = async () => {
console.log('starting fluence...');
await peer.start({
connectTo: relay,
});
await fluence.defaultClient.connect(relay);
console.log('started fluence');
console.log('getting relay time...');
const res = await getRelayTime(relay.peerId);
console.log('got relay time, ', res);
const relayTime = await getRelayTime();
console.log('got relay time, ', relayTime);
console.log('stopping fluence...');
await peer.stop();
await fluence.defaultClient.stop();
console.log('stopped fluence...');
return relayTime;
};
main()
.then(() => console.log('done!'))
.catch((err) => console.error('error: ', err));
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
main().then((res) => {
const inner = document.createElement('div');
inner.id = 'res';
inner.innerText = 'res';
document.getElementById('res-placeholder').appendChild(inner);
});
});

View File

@ -0,0 +1,45 @@
import puppeteer from 'puppeteer';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { startCdn, startContentServer, stopServer } from '@test/test-utils';
const port = 3000;
const uri = `http://localhost:${port}/`;
const __dirname = dirname(fileURLToPath(import.meta.url));
const publicPath = join(__dirname, '../public/');
const test = async () => {
const cdn = await startCdn(8765);
const localServer = await startContentServer(port, publicPath);
console.log('starting puppeteer...');
const browser = await puppeteer.launch();
const page = await browser.newPage();
// uncomment to debug what's happening inside the browser
// page.on('console', (msg) => console.log('// from console: ', msg.text()));
console.log('going to the page in browser...');
await page.goto(uri);
console.log('clicking button...');
await page.click('#btn');
console.log('waiting for result to appear...');
const elem = await page.waitForSelector('#res');
console.log('getting the content of result div...');
const content = await elem?.evaluate((x) => x.textContent);
console.log('raw result: ', content);
await browser.close();
await stopServer(cdn);
await stopServer(localServer);
if (!content) {
throw new Error('smoke test failed!');
}
};
test().then(() => console.log('smoke tests succeed!'));

View File

@ -0,0 +1,7 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist"
},
"exclude": ["node_modules", "dist"]
}

View File

@ -1,4 +0,0 @@
import '@fluencelabs/js-client.node';
import { runMain } from '@test/aqua_for_test';
runMain();

View File

@ -0,0 +1,26 @@
{
"name": "@test/test-utils",
"version": "0.1.0",
"description": "Test utils",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"engines": {
"node": ">=10",
"pnpm": ">=3"
},
"type": "module",
"scripts": {
"build": "tsc"
},
"repository": "https://github.com/fluencelabs/fluence-js",
"author": "Fluence Labs",
"license": "Apache-2.0",
"dependencies": {
"@fluencelabs/js-client.api": "workspace:^",
"@fluencelabs/js-client.node": "workspace:^",
"serve-handler": "6.1.5"
},
"devDependencies": {
"@types/serve-handler": "6.1.1"
}
}

View File

@ -0,0 +1,37 @@
import handler from 'serve-handler';
import { createServer } from 'http';
import type { Server } from 'http';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const CDN_PUBLIC_PATH = join(__dirname, '../../../client/js-client.web.standalone/dist/');
export const startCdn = (port: number) => startContentServer(port, CDN_PUBLIC_PATH);
export const startContentServer = (port: number, publicDir: string): Promise<Server> => {
const server = createServer((request, response) => {
return handler(request, response, {
public: publicDir,
});
});
return new Promise<Server>((resolve) => {
const result = server.listen(port, () => {
console.log(`server started on port ${port}`);
console.log(`public dir ${publicDir}`);
resolve(result);
});
});
};
export const stopServer = (app: Server): Promise<void> => {
return new Promise<void>((resolve) => {
app.close(() => {
console.log('server stopped');
resolve();
});
});
};

View File

@ -1,8 +1,7 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"moduleResolution": "nodenext"
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"],

557
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff