diff --git a/.gitignore b/.gitignore index 9057e1a2..c781037c 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,7 @@ coverage/ venv/ ENV/ env/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/jest.config.js b/jest.config.js index 73ad2eda..549462c7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,9 +7,15 @@ module.exports = { collectCoverage: true, collectCoverageFrom: [ - "src/**/*.{js,jsx}", - "src/tests/**/*.{js,jsx}" + "src/**/*.{js,jsx}", + "!src/**/*.test.{js,jsx}", + "!src/tests/**/*.{js,jsx}" ], - coverageReporters: ['text', 'lcov'], + coverageReporters: ['text', 'lcov', 'json', 'html'], coverageDirectory: "coverage", + + testPathIgnorePatterns: [ + "/node_modules/", + "/playwright_e2e/" + ], }; diff --git a/package.json b/package.json index 5ceb7ad5..c5277704 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "scripts": { "start": "webpack --mode development && electron . --development", "build": "webpack --mode production && electron .", - "test": "jest --coverage --coverageDirectory=coverage", + "test": "jest --coverage --coverageDirectory=coverage", + "test:jest": "jest --coverage --coverageDirectory=coverage", + "test:playwright": "npx playwright test", "postinstall": "electron-builder install-app-deps", "pack": "electron-builder --dir", "dist": "pyinstaller --distpath backend --workpath dist -y --clean -n restapi_server.exe --onefile backend/restapi_server.py && webpack --mode production && electron-builder -p never" @@ -91,7 +93,7 @@ "@testing-library/user-event": "^14.5.2", "babel-jest": "^29.7.0", "babel-loader": "^9.1.3", - "babel-plugin-istanbul": "^7.0.0", + "babel-plugin-istanbul": "^7.0.0", "css-loader": "^6.10.0", "electron": "^28.2.2", "electron-builder": "^24.13.3", @@ -105,7 +107,8 @@ "react-test-renderer": "^18.3.1", "style-loader": "^3.3.4", "webpack": "^5.90.1", - "webpack-cli": "^5.1.4" + "webpack-cli": "^5.1.4", + "playwright": "^1.39.2" }, "dependencies": { "antd": "^5.15.4", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 00000000..7a7afaed --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,42 @@ +const { defineConfig, devices } = require('@playwright/test'); + +module.exports = defineConfig({ + testDir: './playwright_e2e', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: 'http://127.0.0.1:8081', + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], + webServer: { + command: 'npm run playwright-server', + url: 'http://127.0.0.1:8081', + reuseExistingServer: !process.env.CI, + }, +}); + +module.exports = { + testDir: './playwright_e2e', + timeout: 60000, + retries: 1, + use: { + headless: false, + }, +}; diff --git a/playwright_e2e/ACPU.test.js b/playwright_e2e/ACPU.test.js new file mode 100644 index 00000000..f69800af --- /dev/null +++ b/playwright_e2e/ACPU.test.js @@ -0,0 +1,37 @@ +const { _electron: electron } = require('playwright'); +const { test, expect } = require('@playwright/test'); + +test('Launch Electron app, select device, toggle ACPU power, and click Add slowly', async () => { + const app = await electron.launch({ args: ['main.js'] }); + + const window = await app.firstWindow(); + + // selecting the device (MPW1 Gemini) + const deviceDropdown = await window.waitForSelector('#deviceId'); + await deviceDropdown.selectOption('MPW1'); + await new Promise((resolve) => setTimeout(resolve, 2000)); // wait 2 seconds (not really needed, just for demo) + + // clicking on ACPU block + const acpuBlock = await window.waitForSelector('#app > div > div.top-row-container > div.main-table-container.main-border > div.top-l2 > div.top-l2-col1 > div.top-l2-col1-row1 > div:nth-child(1) > div'); + await acpuBlock.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // toggling ACPU power, basically turning on the power on + const acpuPowerToggle = await window.waitForSelector('#app > div > div.table-container.main-border > div > div.toggle-container > label.toggle-switch > span'); + await acpuPowerToggle.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // Click on Add button + const addButton = await window.waitForSelector('#app > div > div.table-container.main-border > div > div.cpu-container > div.table-wrapper > button'); + await addButton.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // Click OK in the popup + const okButton = await window.waitForSelector('body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-footer > button.ant-btn.css-dev-only-do-not-override-ni1kz0.ant-btn-primary.ant-btn-color-primary.ant-btn-variant-solid'); + await okButton.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + console.log('Test case executed successfully.'); + + await app.close(); +}); diff --git a/playwright_e2e/BCPU.test.js b/playwright_e2e/BCPU.test.js new file mode 100644 index 00000000..0980dc0e --- /dev/null +++ b/playwright_e2e/BCPU.test.js @@ -0,0 +1,44 @@ +const { _electron: electron } = require('playwright'); +const { test, expect } = require('@playwright/test'); + +test('Launch Electron app, select device MPW1 Gemini, and click on BCPU block', async () => { + const app = await electron.launch({ args: ['main.js'], headless: false }); + + const window = await app.firstWindow(); + + // selecting MPW1 Gemini from device dropdown + const deviceDropdown = await window.waitForSelector('#deviceId'); + await deviceDropdown.selectOption('MPW1'); + await window.waitForTimeout(2000); // wait for UI update + + // click on the BCPU block + const bcpuSelector = '#app > div > div.top-row-container > div.main-table-container.main-border > div.top-l2 > div.top-l2-col1 > div.top-l2-col1-row1 > div:nth-child(2) > div'; + await window.waitForSelector(bcpuSelector); + await window.click(bcpuSelector); + + // click on "Add" button + const addButtonSelector = '#app > div > div.table-container.main-border > div > div.cpu-container > div.table-wrapper > button'; + await window.waitForSelector(addButtonSelector, { state: 'visible' }); + await window.click(addButtonSelector); + + // click on "OK" button + const okButtonSelector = 'button.ant-btn-primary'; + await window.waitForSelector(okButtonSelector, { state: 'visible' }); + await window.click(okButtonSelector); + + // click on Peripherals tab + const peripheralsTabSelector = '#app > div > div.top-row-container > div.main-table-container.main-border > div.top-l2 > div.top-l2-col1 > div:nth-child(3) > div > div:nth-child(2) > div:nth-child(2) > div.periph-internal-font-header'; + await window.waitForSelector(peripheralsTabSelector, { state: 'visible' }); + await window.click(peripheralsTabSelector); + + // check SPI/QSPI block + const spiQspiCheckSelector = '#\\30'; + await window.waitForSelector(spiQspiCheckSelector, { state: 'visible' }); + await window.click(spiQspiCheckSelector); + + // waiting to observe result on UI + await window.waitForTimeout(5000); + + // closing RPE + await app.close(); +}); diff --git a/playwright_e2e/DSP.test.js b/playwright_e2e/DSP.test.js new file mode 100644 index 00000000..28e72f92 --- /dev/null +++ b/playwright_e2e/DSP.test.js @@ -0,0 +1,105 @@ +const { _electron: electron } = require('playwright'); +const { test, expect } = require('@playwright/test'); + +test('Launch Electron app, add clocking source, navigate to DSP block, configure DSP, and submit form', async () => { + const app = await electron.launch({ args: ['main.js'] }); + const window = await app.firstWindow(); + + // Selecting the device (MPW1 Gemini) + const deviceDropdown = await window.waitForSelector('#deviceId'); + await deviceDropdown.selectOption('MPW1'); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Selecting Clocking block + const clockingBlockSelector = '#app > div > div.top-row-container > div.main-table-container.main-border > div.top-l2 > div.top-l2-col2 > div.top-l2-col2-elem > div > div:nth-child(2) > div:nth-child(1) > div'; + const clockingBlock = await window.waitForSelector(clockingBlockSelector); + await clockingBlock.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Clicking on Add button for Clocking + const addButtonSelector = '#app > div > div.table-container.main-border > div > div.power-and-table-wrapper > div.table-wrapper > button'; + const addButton = await window.waitForSelector(addButtonSelector); + await addButton.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Ensure modal is visible before interacting + const modalSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div'; + await window.waitForSelector(modalSelector, { state: 'visible', timeout: 5000 }); // Wait for modal + + // Typing description as 'test' + const descriptionSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(2) > input[type=text]'; + const descriptionInput = await window.waitForSelector(descriptionSelector); + await descriptionInput.click(); + await descriptionInput.fill('test'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Typing Port/Signal name as 'test' + const portSignalSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(3) > input[type=text]'; + const portSignalInput = await window.waitForSelector(portSignalSelector); + await portSignalInput.click(); + await portSignalInput.fill('test'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Clicking OK to submit the clocking form + const okButtonSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-footer > button.ant-btn.css-dev-only-do-not-override-ni1kz0.ant-btn-primary.ant-btn-color-primary.ant-btn-variant-solid'; + const okButton = await window.waitForSelector(okButtonSelector); + await okButton.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for the form to submit + + // Navigate to the DSP block + const dspBlockSelector = '#app > div > div.top-row-container > div.main-table-container.main-border > div.top-l2 > div.top-l2-col2 > div.top-l2-col2-elem > div > div:nth-child(3) > div:nth-child(2) > div'; + const dspBlock = await window.waitForSelector(dspBlockSelector); + await dspBlock.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Clicking on Add button for DSP + const addDSPButton = await window.waitForSelector(addButtonSelector); // Reusing the same addButtonSelector + await addDSPButton.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Typing Name/Hierarchy as 'test' + const nameHierarchySelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(1) > input[type=text]'; + const nameHierarchyInput = await window.waitForSelector(nameHierarchySelector); + await nameHierarchyInput.click(); + await nameHierarchyInput.fill('test'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Typing XX as 32 + const xxSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(2) > input[type=number]'; + const xxInput = await window.waitForSelector(xxSelector); + await xxInput.click(); + await xxInput.fill('32'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Typing A-input width as 64 + const aInputWidthSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(4) > input[type=number]'; + const aInputWidthInput = await window.waitForSelector(aInputWidthSelector); + await aInputWidthInput.click(); + await aInputWidthInput.fill('64'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Typing B-input width as 64 + const bInputWidthSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(5) > input[type=number]'; + const bInputWidthInput = await window.waitForSelector(bInputWidthSelector); + await bInputWidthInput.click(); + await bInputWidthInput.fill('64'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Typing toggle rate as 50 + const toggleRateSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(8) > input[type=number]'; + const toggleRateInput = await window.waitForSelector(toggleRateSelector); + await toggleRateInput.click(); + await toggleRateInput.fill('50'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Take note of the DSP power generated (assuming it's shown in the UI somewhere, you can add the selector for DSP power if needed) + + // Clicking OK to submit the DSP form + const dspOkButtonSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-footer > button.ant-btn.css-dev-only-do-not-override-ni1kz0.ant-btn-primary.ant-btn-color-primary.ant-btn-variant-solid'; + const dspOkButton = await window.waitForSelector(dspOkButtonSelector); + await dspOkButton.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for the form to submit + + // Closing the test + await app.close(); +}); diff --git a/playwright_e2e/FLE.test.js b/playwright_e2e/FLE.test.js new file mode 100644 index 00000000..c099bc8f --- /dev/null +++ b/playwright_e2e/FLE.test.js @@ -0,0 +1,25 @@ +const { _electron: electron } = require('playwright'); +const { test, expect } = require('@playwright/test'); + +test('Launch Electron app and click on FLE block', async () => { + const app = await electron.launch({ args: ['main.js'] }); + + const window = await app.firstWindow(); + + // selecting the device (MPW1 Gemini) + const deviceDropdown = await window.waitForSelector('#deviceId'); + await deviceDropdown.selectOption('MPW1'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const fleBlock = await window.waitForSelector('#app > div > div.top-row-container > div.main-table-container.main-border > div.top-l2 > div.top-l2-col2 > div.top-l2-col2-elem > div > div:nth-child(2) > div:nth-child(2) > div'); + await fleBlock.click(); + + const flePowerVisible = await window.isVisible('div.title-comp-total-text'); + expect(flePowerVisible).toBeTruthy(); + + console.log('FLE block clicked and verified.'); + + await new Promise((resolve) => setTimeout(resolve, 5000)); + + await app.close(); +}); diff --git a/playwright_e2e/clocking.test.js b/playwright_e2e/clocking.test.js new file mode 100644 index 00000000..43040a16 --- /dev/null +++ b/playwright_e2e/clocking.test.js @@ -0,0 +1,51 @@ +const { _electron: electron } = require('playwright'); +const { test, expect } = require('@playwright/test'); + +test('Launch Electron app, select device, toggle ACPU power, click Clocking, Add clock source, and submit form', async () => { + const app = await electron.launch({ args: ['main.js'] }); + const window = await app.firstWindow(); + + // Selecting the device (MPW1 Gemini) + const deviceDropdown = await window.waitForSelector('#deviceId'); + await deviceDropdown.selectOption('MPW1'); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Selecting Clocking block + const clockingBlockSelector = '#app > div > div.top-row-container > div.main-table-container.main-border > div.top-l2 > div.top-l2-col2 > div.top-l2-col2-elem > div > div:nth-child(2) > div:nth-child(1) > div'; + const clockingBlock = await window.waitForSelector(clockingBlockSelector); + await clockingBlock.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Clicking on Add button + const addButtonSelector = '#app > div > div.table-container.main-border > div > div.power-and-table-wrapper > div.table-wrapper > button'; + const addButton = await window.waitForSelector(addButtonSelector); + await addButton.click(); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds + + // Ensure modal is visible before interacting + const modalSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div'; + await window.waitForSelector(modalSelector, { state: 'visible', timeout: 5000 }); // Wait for modal + + // Typing description as 'test' + const descriptionSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(2) > input[type=text]'; + const descriptionInput = await window.waitForSelector(descriptionSelector); + await descriptionInput.click(); + await descriptionInput.fill('test'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Typing Port/Signal name as 'test' + const portSignalSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-body > div > form > div:nth-child(3) > input[type=text]'; + const portSignalInput = await window.waitForSelector(portSignalSelector); + await portSignalInput.click(); + await portSignalInput.fill('test'); + await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second + + // Clicking OK to submit the form + const okButtonSelector = 'body > div:nth-child(3) > div > div.ant-modal-wrap > div > div:nth-child(1) > div > div.ant-modal-footer > button.ant-btn.css-dev-only-do-not-override-ni1kz0.ant-btn-primary.ant-btn-color-primary.ant-btn-variant-solid'; + const okButton = await window.waitForSelector(okButtonSelector); + await okButton.click(); + + // Closing the test + await app.close(); +}); +