From 2383566dd47224096844d9cd6e490037dc51d998 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Tue, 28 Jan 2025 11:48:13 +0100 Subject: [PATCH 01/36] fix identity manager Visual tests and add to the list --- scripts/pit/its/cc-identity-management.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index c573c6eb..3b38198c 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -40,11 +40,10 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('link', { name: 'Identity Management' }).click(); await page.getByRole('button', { name: 'Enable Identity Management' }).click(); await takeScreenshot(page, __filename, 'app-updated'); - await takeScreenshot(page, __filename, 'enabled'); + await takeScreenshot(page, __filename, 'identity-management-enabled'); await page.getByRole('link', { name: 'Roles' }).click(); await page.getByRole('button', { name: 'Create' }).click(); - await takeScreenshot(page, __filename, 'role-form'); await page.getByLabel('Name').fill(role); await page.getByLabel('Description').fill(role); await takeScreenshot(page, __filename, 'role-filled'); @@ -53,7 +52,6 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('link', { name: 'Groups' }).click(); await page.getByRole('button', { name: 'Create' }).click(); - await takeScreenshot(page, __filename, 'group-form'); await page.getByLabel('Name').fill(group); await page.locator(checkboxSelectorRole).click(); await takeScreenshot(page, __filename, 'group-filled'); @@ -62,7 +60,6 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('link', { name: 'Users' }).click(); await page.getByRole('button', { name: 'Create' }).click(); - await takeScreenshot(page, __filename, 'user-form'); await page.getByLabel('First Name').fill(role); await page.getByLabel('Last Name').fill('user'); await page.getByLabel('E-mail Address').fill(user); @@ -72,7 +69,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('contentinfo').getByRole('button', { name: 'Create' }).click(); await takeScreenshot(page, __filename, 'user-created'); - await waitForServerReady(page, url); + await page.goto(url); await takeScreenshot(page, __filename, `app-${app}-loaded`); log(`Logging in ${app} as ${user} ...\n`); From a1822d9697a54445bb350d8b4acc0d70e4a29e16 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Tue, 28 Jan 2025 12:01:22 +0100 Subject: [PATCH 02/36] add more screenshots --- scripts/pit/its/cc-identity-management.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 3b38198c..2f82dbf3 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -40,10 +40,11 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('link', { name: 'Identity Management' }).click(); await page.getByRole('button', { name: 'Enable Identity Management' }).click(); await takeScreenshot(page, __filename, 'app-updated'); - await takeScreenshot(page, __filename, 'identity-management-enabled'); + await takeScreenshot(page, __filename, 'enabled'); await page.getByRole('link', { name: 'Roles' }).click(); await page.getByRole('button', { name: 'Create' }).click(); + await takeScreenshot(page, __filename, 'role-form'); await page.getByLabel('Name').fill(role); await page.getByLabel('Description').fill(role); await takeScreenshot(page, __filename, 'role-filled'); @@ -52,6 +53,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('link', { name: 'Groups' }).click(); await page.getByRole('button', { name: 'Create' }).click(); + await takeScreenshot(page, __filename, 'group-form'); await page.getByLabel('Name').fill(group); await page.locator(checkboxSelectorRole).click(); await takeScreenshot(page, __filename, 'group-filled'); @@ -60,6 +62,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('link', { name: 'Users' }).click(); await page.getByRole('button', { name: 'Create' }).click(); + await takeScreenshot(page, __filename, 'user-form'); await page.getByLabel('First Name').fill(role); await page.getByLabel('Last Name').fill('user'); await page.getByLabel('E-mail Address').fill(user); From 51757ebab9bb4563b33a87847f509cd46a7f30f4 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Tue, 28 Jan 2025 12:14:37 +0100 Subject: [PATCH 03/36] wait for server ready when connecting to deployed app --- scripts/pit/its/cc-identity-management.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 2f82dbf3..c573c6eb 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -72,7 +72,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('contentinfo').getByRole('button', { name: 'Create' }).click(); await takeScreenshot(page, __filename, 'user-created'); - await page.goto(url); + await waitForServerReady(page, url); await takeScreenshot(page, __filename, `app-${app}-loaded`); log(`Logging in ${app} as ${user} ...\n`); From 6bf265f289804ff1cf9ea4f39c8d9b5532e2aee2 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Tue, 28 Jan 2025 21:44:29 +0100 Subject: [PATCH 04/36] fix localization Visual tests and add to the list --- scripts/pit/its/cc-identity-management.js | 7 +- scripts/pit/its/cc-localization.js | 142 ++++++++++++---------- scripts/pit/its/test-utils.js | 2 +- scripts/pit/lib/lib-ccenter.sh | 2 +- 4 files changed, 82 insertions(+), 71 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index c573c6eb..cb19ae69 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -1,8 +1,6 @@ const { expect} = require('@playwright/test'); -const fs = require('fs'); const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = require('./test-utils'); - (async () => { const arg = args(); if (!arg.login) { @@ -23,7 +21,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await expect(page.getByLabel('Email')).toBeVisible(); await takeScreenshot(page, __filename, 'view-loaded'); - log(`Logging in as ${arg.login} ${arg.pass}...\n`); + log(`Logging in CC as ${arg.login} ${arg.pass}...\n`); await page.getByLabel('Email').fill(arg.login); await page.getByLabel('Password').fill(arg.pass); await page.getByRole('button', {name: 'Sign In'}).click() @@ -35,11 +33,10 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r log(`App: ${app} installed in: ${url}\n`); await page.locator('vaadin-select vaadin-input-container div').click(); - await page.getByRole('option', { name: 'bakery-cc' }).locator('div').nth(2).click(); + await page.getByRole('option', { name: app }).locator('div').nth(2).click(); await takeScreenshot(page, __filename, 'selected-app'); await page.getByRole('link', { name: 'Identity Management' }).click(); await page.getByRole('button', { name: 'Enable Identity Management' }).click(); - await takeScreenshot(page, __filename, 'app-updated'); await takeScreenshot(page, __filename, 'enabled'); await page.getByRole('link', { name: 'Roles' }).click(); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 382f5ce9..48ff2284 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -1,74 +1,88 @@ -const {chromium} = require('playwright'); -const sleep = ms => new Promise(r => setTimeout(r, ms)); +const { expect} = require('@playwright/test'); +const fs = require('fs'); const path = require('path'); -const {expect} = require('@playwright/test'); - -const ADMIN_EMAIL = 'john.doe@admin.com'; -const ADMIN_PASSWORD = 'adminPassword'; - -let headless = false, host = 'localhost', port = '8000', hub = false; -process.argv.forEach(a => { - if (/^--headless/.test(a)) { - headless = true; - } else if (/^--ip=/.test(a)) { - ip = a.split('=')[1]; - } else if (/^--port=/.test(a)) { - port = a.split('=')[1]; - } -}); +const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, run} = require('./test-utils'); +const { assert } = require('console'); (async () => { - const browser = await chromium.launch({ - headless: headless, - chromiumSandbox: false - }); - const context = await browser.newContext({ignoreHTTPSErrors: true}); - - // Open new page - const page = await context.newPage(); - page.on('console', msg => console.log("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '))); - page.on('pageerror', err => console.log("> PAGEERROR:", ('' + err).replace(/\s+/g, ' '))); - - // Go to http://${host}:${port}/ - await page.goto(`http://${host}:${port}/`); - - await page.getByLabel('Email').fill(ADMIN_EMAIL) - await page.getByLabel('Password', {exact: true}).fill(ADMIN_PASSWORD) - await page.getByRole('button', {name: 'Sign In'}).click() - - await page.goto(`http://${host}:${port}/settings/apps/app1`); - - const locOpt = page.getByLabel('Localization').click(); - if(await !page.getByLabel('Localization').isChecked()){ - await page.getByLabel('Localization').click({timeout:60000}) + const arg = args(); + if (!arg.login) { + log(`Skipping the setup of Control center because of missing --email= parameter\n`) + process.exit(1); } + const app = `bakery-cc`; + const user = 'admin@vaadin.com'; + const password = 'admin'; + const downloadsDir = './downloads'; + const propsFile = 'translations.properties'; - await page.getByRole('button', {name: 'Update'}).click() - - await page.goto(`http://${host}:${port}/app/app1/i18n/translations`); - - await page.getByRole('menuitem').click() - await page.getByText('Upload translations').click() + const page = await createPage(arg.headless, arg.ignoreHTTPSErrors); + await waitForServerReady(page, arg.url); + await expect(page.getByLabel('Email')).toBeVisible(); + await takeScreenshot(page, __filename, 'view-loaded'); + log(`Logging in CC as ${arg.login} ${arg.pass}...\n`); + await page.getByLabel('Email').fill(arg.login); + await page.getByLabel('Password').fill(arg.pass); + await page.getByRole('button', {name: 'Sign In'}).click() + await takeScreenshot(page, __filename, 'logged-in'); + + await page.getByRole('link', { name: 'Settings', }).click(); + await takeScreenshot(page, __filename, 'settings'); + const anchorSelectorURL = `//vaadin-grid-cell-content[.//span[normalize-space(text())="${app}"]]//a`; + const url = await page.locator(anchorSelectorURL).getAttribute('href'); + const previewUrl = url.replace(/:\/\//, '://preview.'); + log(`App: ${app} installed in: ${url} preview: ${previewUrl}\n`); + + await page.locator('vaadin-select vaadin-input-container div').click(); + await page.getByRole('option', { name: app }).locator('div').nth(2).click(); + await takeScreenshot(page, __filename, 'selected-app'); + + await page.getByRole('link', { name: 'Localization' }).click(); + await page.getByRole('button', { name: 'Enable Localization' }).click(); + await takeScreenshot(page, __filename, 'enabled'); + + fs.writeFileSync(propsFile, 'app.title=Bakery\n'); + await page.getByLabel('Manage translations').locator('svg').click(); + await page.getByText('Upload translations').click(); + await page.getByLabel('I understand that this will').check(); const fileChooserPromise = page.waitForEvent('filechooser'); - await page.getByText('Upload Files...').click(); + await page.getByRole('button', { name: 'Upload Files...' }).click(); const fileChooser = await fileChooserPromise; - await fileChooser.setFiles(path.join(__dirname, 'translations.properties')); - await page.getByLabel('I understand that this will replace all corresponding data.').check() - await page.getByRole('button', {name: 'Replace data'}).click() - - await page.getByText('Hello anonymous!').click(); - await page.locator('vaadin-grid-cell-content').getByRole('textbox').fill('Test'); - - await page.getByText('Say hello').click(); - await page.locator('vaadin-grid-cell-content').getByRole('textbox').fill('Say bonjour'); - await page.locator('.confirm-button').click(); - - expect(page.getByText('Hello anonymous!')).toBeVisible() - expect(page.locator('vaadin-grid-cell-content').filter({ hasText: 'Say bonjour' })).toBeVisible() - - // --------------------- - await context.close(); - await browser.close(); + await fileChooser.setFiles(propsFile); + await page.getByRole('button', { name: 'Replace data' }).click(); + fs.unlinkSync(propsFile); + + await page.getByText('Bakery', { exact: true }).click(); + await page.locator('vaadin-text-area.inline textarea').fill('Panaderia'); + await page.locator('vaadin-grid').getByRole('button').first().click(); + + await page.getByLabel('Manage translations').locator('svg').click(); + const downloadPromise = page.waitForEvent('download'); + await page.getByText('Download translations').click(); + const download = await downloadPromise; + const filePath = `${downloadsDir}/${download.suggestedFilename()}`; + await download.saveAs(filePath); + + await run(`unzip -d ${downloadsDir} -o ${filePath}`); + const str = await fs.readFileSync('./downloads/translations.properties', 'utf8'); + assert(str.includes('app.title=Panaderia')); + await fs.rmdirSync(downloadsDir, { recursive: true }); + + await page.getByRole('button', { name: 'Start preview' }).click(); + + log(`Logging in Preview CC as ${user} ${password}...\n`); + const pagePrev = await createPage(arg.headless, true); + await waitForServerReady(pagePrev, previewUrl); + await pagePrev.getByLabel('Email').fill(user); + await pagePrev.getByLabel('Password').fill(password); + await pagePrev.getByRole('button', {name: 'Sign In'}).click() + await takeScreenshot(pagePrev, __filename, 'preview-logged-in'); + await expect(pagePrev.getByRole('button', { name: 'New order' })).toBeVisible(); + + await page.getByRole('button', { name: 'Stop preview' }).click(); + + await closePage(pagePrev); + await closePage(page); })(); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index 7a585be9..68158341 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -85,7 +85,7 @@ async function waitForServerReady(page, url, options = {}) { try { const response = await page.goto(url); // Check if the response status is not 503 - if (response && response.status() !== 503) { + if (response && response.status() < 400) { log(`Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); return response; } else { diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 47016a7a..898e506e 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -19,7 +19,7 @@ CC_CLUSTER=cc-cluster CC_NS=control-center ## UI tests to run after the control-center is installed -CC_TESTS="cc-setup.js cc-install-apps.js cc-identity-management.js" +CC_TESTS="cc-setup.js cc-install-apps.js cc-identity-management.js cc-localization.js" checkDockerRunning() { if ! docker ps > /dev/null 2>&1; then From 1d3aa7c30d7d66ba91622b12f5d8dd0229a1a01f Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 08:26:33 +0100 Subject: [PATCH 05/36] check that app is running before enabling features, and clean up --- scripts/pit/its/cc-identity-management.js | 50 ++++++++++++++++++----- scripts/pit/its/cc-localization.js | 42 ++++++++++++++----- scripts/pit/its/test-utils.js | 29 +++++++++---- 3 files changed, 92 insertions(+), 29 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index cb19ae69..bb7d0e1e 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -27,18 +27,28 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('button', {name: 'Sign In'}).click() await takeScreenshot(page, __filename, 'logged-in'); + log(`Changing Settings for ${app}...\n`); await page.getByRole('link', { name: 'Settings', }).click(); await takeScreenshot(page, __filename, 'settings'); const url = await page.locator(anchorSelectorURL).getAttribute('href'); - log(`App: ${app} installed in: ${url}\n`); + log(`Checking that ${app} installed in ${url} is running ...\n`); + // When app is not running, localization cannot be enabled + let pageApp = await createPage(arg.headless, true); + await waitForServerReady(pageApp, url); + await closePage(pageApp); + await takeScreenshot(page, __filename, 'app-running'); + + log(`Enabling identity Management ...\n`); await page.locator('vaadin-select vaadin-input-container div').click(); await page.getByRole('option', { name: app }).locator('div').nth(2).click(); await takeScreenshot(page, __filename, 'selected-app'); + await page.getByRole('link', { name: 'Identity Management' }).click(); await page.getByRole('button', { name: 'Enable Identity Management' }).click(); - await takeScreenshot(page, __filename, 'enabled'); + await takeScreenshot(page, __filename, 'identity-enabled'); + log(`Adding Role, Group and User ...\n`); await page.getByRole('link', { name: 'Roles' }).click(); await page.getByRole('button', { name: 'Create' }).click(); await takeScreenshot(page, __filename, 'role-form'); @@ -69,15 +79,35 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await page.getByRole('contentinfo').getByRole('button', { name: 'Create' }).click(); await takeScreenshot(page, __filename, 'user-created'); - await waitForServerReady(page, url); - await takeScreenshot(page, __filename, `app-${app}-loaded`); - log(`Logging in ${app} as ${user} ...\n`); - await page.getByLabel('Email').fill(user); - await page.getByLabel('Password').fill(role); - await page.getByRole('button', {name: 'Sign In'}).click() - await takeScreenshot(page, __filename, `logged-in-${app}`); - await expect(page.getByRole('button', { name: 'New order' })).toBeVisible(); + pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); + await waitForServerReady(pageApp, url); + await takeScreenshot(pageApp, __filename, `app-${app}-loaded`); + await pageApp.getByLabel('Email').fill(user); + await pageApp.getByLabel('Password').fill(role); + await pageApp.getByRole('button', {name: 'Sign In'}).click() + await takeScreenshot(pageApp, __filename, `logged-in-${app}`); + await expect(pageApp.getByRole('button', { name: 'New order' })).toBeVisible(); + await closePage(pageApp); + + log('Cleaning up...\n'); + await page.getByRole('link', { name: 'Roles' }).click(); + await page.getByText('admin').nth(1).click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.getByRole('link', { name: 'Groups' }).click(); + await page.getByText('admin', { exact: true }).click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.getByRole('link', { name: 'Users' }).click(); + await page.getByText('admin user').click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.getByRole('link', { name: 'Settings' }).click(); + await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); + await page.getByLabel('Identity Management').uncheck(); + await page.getByRole('button', { name: 'Disable' }).click(); + await page.getByRole('button', { name: 'Update' }).click(); await closePage(page); })(); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 48ff2284..3f28788f 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -28,20 +28,28 @@ const { assert } = require('console'); await page.getByRole('button', {name: 'Sign In'}).click() await takeScreenshot(page, __filename, 'logged-in'); + log(`Changing Settings for ${app}...\n`); await page.getByRole('link', { name: 'Settings', }).click(); await takeScreenshot(page, __filename, 'settings'); const anchorSelectorURL = `//vaadin-grid-cell-content[.//span[normalize-space(text())="${app}"]]//a`; const url = await page.locator(anchorSelectorURL).getAttribute('href'); const previewUrl = url.replace(/:\/\//, '://preview.'); - log(`App: ${app} installed in: ${url} preview: ${previewUrl}\n`); + log(`Checking that ${app} installed in ${url} is running ...\n`); + // When app is not running, localization cannot be enabled + const pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); + await waitForServerReady(pageApp, url); + await closePage(pageApp); + await takeScreenshot(page, __filename, 'app-running'); + + log(`Uploading and updating localization keys ...\n`); await page.locator('vaadin-select vaadin-input-container div').click(); await page.getByRole('option', { name: app }).locator('div').nth(2).click(); await takeScreenshot(page, __filename, 'selected-app'); await page.getByRole('link', { name: 'Localization' }).click(); await page.getByRole('button', { name: 'Enable Localization' }).click(); - await takeScreenshot(page, __filename, 'enabled'); + await takeScreenshot(page, __filename, 'localization-enabled'); fs.writeFileSync(propsFile, 'app.title=Bakery\n'); await page.getByLabel('Manage translations').locator('svg').click(); @@ -54,10 +62,13 @@ const { assert } = require('console'); await page.getByRole('button', { name: 'Replace data' }).click(); fs.unlinkSync(propsFile); + await takeScreenshot(page, __filename, 'localization-loaded'); await page.getByText('Bakery', { exact: true }).click(); await page.locator('vaadin-text-area.inline textarea').fill('Panaderia'); await page.locator('vaadin-grid').getByRole('button').first().click(); + await takeScreenshot(page, __filename, 'localization-changed'); + log(`Downloading and checking localization keys ...\n`); await page.getByLabel('Manage translations').locator('svg').click(); const downloadPromise = page.waitForEvent('download'); await page.getByText('Download translations').click(); @@ -70,19 +81,30 @@ const { assert } = require('console'); assert(str.includes('app.title=Panaderia')); await fs.rmdirSync(downloadsDir, { recursive: true }); + log(`Testing that preview page: ${previewUrl} is up and running\n`); await page.getByRole('button', { name: 'Start preview' }).click(); - - log(`Logging in Preview CC as ${user} ${password}...\n`); const pagePrev = await createPage(arg.headless, true); await waitForServerReady(pagePrev, previewUrl); - await pagePrev.getByLabel('Email').fill(user); - await pagePrev.getByLabel('Password').fill(password); - await pagePrev.getByRole('button', {name: 'Sign In'}).click() - await takeScreenshot(pagePrev, __filename, 'preview-logged-in'); - await expect(pagePrev.getByRole('button', { name: 'New order' })).toBeVisible(); + await takeScreenshot(page, __filename, 'preview-ready'); + const text = await pagePrev.getByText(/Password|Dashboard/).textContent(); + if (text.includes('Password')) { + await pagePrev.getByLabel('Email').fill(user); + await pagePrev.getByLabel('Password').fill(password); + await pagePrev.getByRole('button', {name: 'Sign In'}).click() + await takeScreenshot(pagePrev, __filename, 'preview-logged-in'); + await expect(pagePrev.getByRole('button', { name: 'New order' })).toBeVisible(); + await takeScreenshot(page, __filename, 'preview-loaded'); + } + // await expect(pagePrev.getByText('Panaderia', { exact: true })).toBeVisible(); + await closePage(pagePrev); + log('Cleaning up...\n'); await page.getByRole('button', { name: 'Stop preview' }).click(); + await page.getByRole('link', { name: 'Settings' }).click(); + await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); + await page.getByLabel('Localization').uncheck(); + await page.getByRole('button', { name: 'Disable' }).click(); + await page.getByRole('button', { name: 'Update' }).click(); - await closePage(pagePrev); await closePage(page); })(); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index 68158341..b96d70c8 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -8,9 +8,19 @@ defineConfig({ expect: {timeout: 30 * 1000}, }); -const log = (s) => process.stderr.write(` ${s}`); +function log(...args) { + process.stderr.write(`\x1b[0m> \x1b[0;32m${args}\x1b[0m`); +} +function out(...args) { + process.stderr.write(`\x1b[2m\x1b[196m${args}\x1b[0m`); +} +function err(...args) { + process.stderr.write(`\x1b[0;31m${args}\x1b[0m`); +} + const run = async (cmd) => (await promisify(exec)(cmd)).stdout; + const args = () => { const ret = { headless: false, @@ -52,8 +62,8 @@ async function createPage(headless, ignoreHTTPSErrors) { }); const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors }); const page = await context.newPage(); - page.on('console', msg => console.log("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '))); - page.on('pageerror', err => console.log("> PAGEERROR:", ('' + err).replace(/\s+/g, ' '))); + page.on('console', msg => out("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '), '\n')); + page.on('pageerror', e => err("> PAGEERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); page.browser = browser; return page; } @@ -70,7 +80,7 @@ async function takeScreenshot(page, name, descr) { const file = `${screenshots}/${scr}-${cnt}-${descr}.png`; await page.waitForTimeout(1000); await page.screenshot({ path: file }); - log(`Screenshot taken: ${file}\n`); + out(`Screenshot taken: ${file}\n`); } // Wait for the server to be ready and to get a valid response @@ -86,17 +96,18 @@ async function waitForServerReady(page, url, options = {}) { const response = await page.goto(url); // Check if the response status is not 503 if (response && response.status() < 400) { - log(`Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); + out(`Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); return response; } else { - log(`Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); + out(`Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); } } catch (error) { if (error.message.includes('net::ERR_CERT_AUTHORITY_INVALID')) { - log(`Attempt ${attempt} Server has not a valid certificate, install it for ${url} or use --notls flag\n`); + err(`Attempt ${attempt} Server has not a valid certificate, install it for ${url} or use --notls flag\n`); } else { - log(`Attempt ${attempt} Server failed with error: ${error.message}\n`); + err(`Attempt ${attempt} Server failed with error: ${error.message}\n`); } + throw new Error(`Server Error ${error}.\n`); } await page.waitForTimeout(retryInterval); } @@ -104,7 +115,7 @@ async function waitForServerReady(page, url, options = {}) { } module.exports = { - log, + log, out, err, run, args, createPage, From 37404ecee594272d3d50a4d2407330b877a668dd Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 13:15:09 +0100 Subject: [PATCH 06/36] fix setsuid in mac --- scripts/pit/lib/lib-args.sh | 2 ++ scripts/pit/lib/lib-ccenter.sh | 5 ++--- scripts/pit/lib/lib-k8s-kind.sh | 39 ++++++++++++++++++++++++++------- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/scripts/pit/lib/lib-args.sh b/scripts/pit/lib/lib-args.sh index 4cd35d4c..ec0732a2 100644 --- a/scripts/pit/lib/lib-args.sh +++ b/scripts/pit/lib/lib-args.sh @@ -21,6 +21,7 @@ Use: $0 with the next options: --skip-prod Skip production validations --skip-dev Skip dev-mode validations --skip-clean Do not clean maven cache + --skip-helm Do not re-install control-center with helm and continue running tests --skip-pw Do not run playwright tests --keep-cc Keep control-center running after tests --pnpm Use pnpm instead of npm to speed up frontend compilation (default npm) @@ -73,6 +74,7 @@ checkArgs() { --skip-dev) NODEV=true;; --skip-prod) NOPROD=true;; --skip-pw) SKIPPW=true;; + --skip-helm) SKIPHELM=true;; --keep-cc) KEEPCC=true;; --pnpm) PNPM="-Dpnpm.enable=true";; --vite) VITE=true;; diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 898e506e..505c987c 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -30,6 +30,7 @@ checkDockerRunning() { ## Install Control Center with Helm installCC() { + [ -n "SKIPHELM" ] && H=`kubectl get pods 2>&1` && echo "$H" | egrep -q 'control-center-[0-9abcdef]+-..... ' && return 0 [ -n "$VERBOSE" ] && D=--debug || D="" [ -n "$CC_KEY" -a -n "$CC_CERT" ] && args="--set app.tlsSecret=$CC_TLS_A --set keycloak.tlsSecret=$CC_TLS_K" || args="" runCmd "$TEST" "Installing Vaadin Control Center" \ @@ -120,7 +121,6 @@ installTls() { pod=`kubectl -n $CC_NS get pods | grep control-center-ingress-nginx-controller | awk '{print $1}'` || return 1 [ -n "$pod" ] && runCmd "$TEST" "Reloading nginx in $pod" "kubectl exec $pod -n "$CC_NS" -- nginx -s reload" || return 1 - runCmd "$TEST" "Waiting for reloading ingress" sleep 5 } ## Show temporary user-email and password in the terminal @@ -149,8 +149,7 @@ runControlCenter() { checkBusyPort "443" || return 1 checkDockerRunning || return 1 ## Clean up from a previous run - # stopCloudProvider - uninstallCC $CC_CLUSTER $CC_NS + [ -z "$SKIPHELM" ] && uninstallCC $CC_CLUSTER $CC_NS # deleteCluster $CC_CLUSTER ## Start a new cluster createCluster $CC_CLUSTER $CC_NS || return 1 diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index f2783623..41c432f8 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -17,16 +17,35 @@ stopCloudProvider() { docker ps | grep envoyproxy/envoy | awk '{print $1}' | xargs docker kill 2>/dev/null } -## +## Check that the command has SUID bit set +# $1: command +hasSUID() { + [ ! -x "$1" ] && return 1 + R=`realpath "$1"` || return 1 + O=`ls -l "$R" | awk '{print $3}'` + P=`ls -l "$R" | awk '{print $1}'` + [ "$O" = "root" ] && expr "$P" : "^-..s" >/dev/null && return 0 || return 1 +} + +## Set SUID bit to the command # $1: command setSuid() { isWindows && return 0 - W=`which $1` || return 1 + for W in "/tmp/$1" `which "$1"`; do + hasSUID "$W" && echo "$W" && alias kubectl="$W" && return 0 + done R=`realpath $W` || return 1 - O=`ls -l "$R" | awk '{print $3}'` - P=`ls -l "$R" | awk '{print $1}'` - [ "$O" = "root" ] || runCmd "$TEST" "Changing owner to root to: $R" "sudo chown root $R" || return 1 - expr "$P" : "^-..s" >/dev/null || runCmd "$TEST" "Setting sUI to $R" "sudo chmod u+s $R" || return 1 + echo "$R" + + sudo -n true >/dev/null 2>&1 || log "It's necessary to provide sudo password to run '$1' as root" + sudo -B true || return 1 + + runCmd "$TEST" "Changing owner to root to: $R" "sudo chown root $R" \ + && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $R" && return 0 + + runCmd "$TEST" "Coping $R" "cp $R /tmp/$1" \ + && runCmd "$TEST" "Changing owner to root to: /tmp/$1" "sudo chown root /tmp/$1" \ + && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $R" && return 0 } ## @@ -38,8 +57,12 @@ startPortForward() { H=`getPids "kubectl port-forward $2"` [ -n "$H" ] && log "Already running k8s port-forward $1 $2 $3 -> $4 with pid $H" && return 0 [ -z "$TEST" ] && log "Starting k8s port-forward $1 $2 $3 -> $4" - [ "$4" -le 1024 ] && setSuid kubectl || return 1 - runInBackgroundToFile "kubectl port-forward $2 $4:$3 -n $1" "k8s-port-forward-$3-$4.log" || return 1 + [ "$4" -le 1024 ] && K=`setSuid kubectl` || return 1 + bgf="k8s-port-forward-$3-$4.log" + rm -f "$bgf" + runInBackgroundToFile "$K port-forward $2 $4:$3 -n $1" "$bgf" + sleep 2 + cat "$bgf" } ## From 3e4290c9bc4c49ba3408c75c6a2834652b1c1988 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 13:32:58 +0100 Subject: [PATCH 07/36] typo --- scripts/pit/lib/lib-ccenter.sh | 2 +- scripts/pit/lib/lib-k8s-kind.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 505c987c..3efa0697 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -19,7 +19,7 @@ CC_CLUSTER=cc-cluster CC_NS=control-center ## UI tests to run after the control-center is installed -CC_TESTS="cc-setup.js cc-install-apps.js cc-identity-management.js cc-localization.js" +CC_TESTS=${CC_TESTS:-cc-setup.js cc-install-apps.js cc-identity-management.js cc-localization.js} checkDockerRunning() { if ! docker ps > /dev/null 2>&1; then diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index 41c432f8..6d6dd716 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -45,7 +45,7 @@ setSuid() { runCmd "$TEST" "Coping $R" "cp $R /tmp/$1" \ && runCmd "$TEST" "Changing owner to root to: /tmp/$1" "sudo chown root /tmp/$1" \ - && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $R" && return 0 + && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s /tmp/$1" && return 0 } ## From 9ab9ea3b4b08f87dd643a8b5628d08c4fe47b9f9 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 13:35:11 +0100 Subject: [PATCH 08/36] add missing sudo --- scripts/pit/lib/lib-k8s-kind.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index 6d6dd716..e2ddb29d 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -43,7 +43,7 @@ setSuid() { runCmd "$TEST" "Changing owner to root to: $R" "sudo chown root $R" \ && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $R" && return 0 - runCmd "$TEST" "Coping $R" "cp $R /tmp/$1" \ + runCmd "$TEST" "Coping $R" "sudo cp $R /tmp/$1" \ && runCmd "$TEST" "Changing owner to root to: /tmp/$1" "sudo chown root /tmp/$1" \ && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s /tmp/$1" && return 0 } From 82f97f3e0c8f8a8036187dcef6d8099f12b3af39 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 13:41:53 +0100 Subject: [PATCH 09/36] set correctly path to kubectl --- scripts/pit/lib/lib-k8s-kind.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index e2ddb29d..b7f1f603 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -39,6 +39,7 @@ setSuid() { sudo -n true >/dev/null 2>&1 || log "It's necessary to provide sudo password to run '$1' as root" sudo -B true || return 1 + sudo rm -f /tmp/$1 runCmd "$TEST" "Changing owner to root to: $R" "sudo chown root $R" \ && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $R" && return 0 @@ -57,12 +58,13 @@ startPortForward() { H=`getPids "kubectl port-forward $2"` [ -n "$H" ] && log "Already running k8s port-forward $1 $2 $3 -> $4 with pid $H" && return 0 [ -z "$TEST" ] && log "Starting k8s port-forward $1 $2 $3 -> $4" - [ "$4" -le 1024 ] && K=`setSuid kubectl` || return 1 + [ "$4" -le 1024 ] && setSuid kubectl || return 1 bgf="k8s-port-forward-$3-$4.log" rm -f "$bgf" + [ -x /tmp/kubectl ] && K=/tmp/kubectl || K=kubectl runInBackgroundToFile "$K port-forward $2 $4:$3 -n $1" "$bgf" sleep 2 - cat "$bgf" + cat "$bgf" | egrep 'Forwarding from' } ## From aaed4af0e9b2a85675040c89a5549bc77d36e028 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 17:09:01 +0100 Subject: [PATCH 10/36] fix quotes in NPM command when node is in .vaadin, improve logs --- scripts/pit/its/cc-install-apps.js | 2 +- scripts/pit/its/cc-localization.js | 2 +- scripts/pit/its/cc-setup.js | 4 ++-- scripts/pit/its/test-utils.js | 23 ++++++++++++++--------- scripts/pit/lib/lib-ccenter.sh | 3 ++- scripts/pit/lib/lib-k8s-kind.sh | 20 +++++++++----------- scripts/pit/lib/lib-playwright.sh | 7 +++---- scripts/pit/lib/lib-utils.sh | 4 ++-- 8 files changed, 34 insertions(+), 31 deletions(-) diff --git a/scripts/pit/its/cc-install-apps.js b/scripts/pit/its/cc-install-apps.js index aec17298..0c0d0a08 100644 --- a/scripts/pit/its/cc-install-apps.js +++ b/scripts/pit/its/cc-install-apps.js @@ -64,7 +64,7 @@ async function installApp(app, page) { await installApp(app, page); } - log(`Waiting for the applications to be available...\n`); + log(`Waiting for 2 applications to be available...\n`); const selector = 'vaadin-grid-cell-content span[theme="badge success"]'; await expect(page.locator(selector).nth(0)).toBeVisible({ timeout: 180000 }); await takeScreenshot(page, __filename, 'app-1-available'); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 3f28788f..0eed5b43 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -79,7 +79,7 @@ const { assert } = require('console'); await run(`unzip -d ${downloadsDir} -o ${filePath}`); const str = await fs.readFileSync('./downloads/translations.properties', 'utf8'); assert(str.includes('app.title=Panaderia')); - await fs.rmdirSync(downloadsDir, { recursive: true }); + await fs.rmSync(downloadsDir, { recursive: true }); log(`Testing that preview page: ${previewUrl} is up and running\n`); await page.getByRole('button', { name: 'Start preview' }).click(); diff --git a/scripts/pit/its/cc-setup.js b/scripts/pit/its/cc-setup.js index ceb04c28..b307c4bd 100644 --- a/scripts/pit/its/cc-setup.js +++ b/scripts/pit/its/cc-setup.js @@ -33,8 +33,8 @@ const {log, run, args, createPage, closePage, takeScreenshot, waitForServerReady await takeScreenshot(page, __filename, 'password-changed'); - await page.getByLabel('First Name').fill(arg.login); - await page.getByLabel('Last Name').fill(arg.login); + await page.getByLabel('First Name').fill(arg.login.split('@')[0]); + await page.getByLabel('Last Name').fill(arg.login.split('@')[1]); await page.getByRole('button', { name: 'Submit' }).click(); await takeScreenshot(page, __filename, 'user-configured'); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index b96d70c8..ec1b9ebe 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -14,6 +14,9 @@ function log(...args) { function out(...args) { process.stderr.write(`\x1b[2m\x1b[196m${args}\x1b[0m`); } +function warn(...args) { + process.stderr.write(`\x1b[2m\x1b[91m${args}\x1b[0m`); +} function err(...args) { process.stderr.write(`\x1b[0;31m${args}\x1b[0m`); } @@ -63,7 +66,7 @@ async function createPage(headless, ignoreHTTPSErrors) { const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors }); const page = await context.newPage(); page.on('console', msg => out("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '), '\n')); - page.on('pageerror', e => err("> PAGEERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); + page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); page.browser = browser; return page; } @@ -80,7 +83,7 @@ async function takeScreenshot(page, name, descr) { const file = `${screenshots}/${scr}-${cnt}-${descr}.png`; await page.waitForTimeout(1000); await page.screenshot({ path: file }); - out(`Screenshot taken: ${file}\n`); + out(` 📸 Screenshot taken: ${file}\n`); } // Wait for the server to be ready and to get a valid response @@ -93,21 +96,23 @@ async function waitForServerReady(page, url, options = {}) { log(`Opening ${url}\n`); for (let attempt = 0; attempt < maxRetries; attempt++) { try { - const response = await page.goto(url); + const response = await page.goto(url, {timeou: 120000}); // Check if the response status is not 503 if (response && response.status() < 400) { - out(`Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); + out(` ⏲ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); return response; } else { - out(`Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); + out(` ⏲ Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); } } catch (error) { if (error.message.includes('net::ERR_CERT_AUTHORITY_INVALID')) { - err(`Attempt ${attempt} Server has not a valid certificate, install it for ${url} or use --notls flag\n`); + err(` ⏲ Attempt ${attempt} Server has not a valid certificate, install it for ${url} or use --notls flag\n`); } else { - err(`Attempt ${attempt} Server failed with error: ${error.message}\n`); + err(` ⏲ Attempt ${attempt} Server failed with error: ${error.message}\n`); + } + if (attempt >= 10) { + throw new Error(`Server Error ${error}.\n`); } - throw new Error(`Server Error ${error}.\n`); } await page.waitForTimeout(retryInterval); } @@ -115,7 +120,7 @@ async function waitForServerReady(page, url, options = {}) { } module.exports = { - log, out, err, + log, out, err, warn, run, args, createPage, diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 3efa0697..77b0fdfb 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -77,7 +77,7 @@ uninstallCC() { } checkTls() { - [ -n "$TEST" ] && return 0 + [ -n "$TEST" -o -n "$SKIPHELM" ] && return 0 for i in `kubectl get ingresses -n $CC_NS | grep nginx | awk '{print $1}'`; do log "$i" H=`kubectl get ingress $i -n $CC_NS -o jsonpath='{.spec.rules[0].host}'` @@ -90,6 +90,7 @@ checkTls() { ## Configure secrets for the control-center and the keycloak servers installTls() { + [ -n "$SKIPHELM" ] && return 0 [ -z "$CC_KEY" -o -z "$CC_CERT" ] && log "No CC_KEY and CC_CERT provided, skiping TLS installation" && return 0 [ -n "$CC_FULL" ] && CC_CERT=$CC_FULL [ -z "$TEST" ] && log "Installing TLS $CC_TLS for $CC_CONTROL and $CC_AUT" || cmd "## Creating TLS file '$CC_DOMAIN.pem' from envs" diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index b7f1f603..7ecd9617 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -31,22 +31,21 @@ hasSUID() { # $1: command setSuid() { isWindows && return 0 - for W in "/tmp/$1" `which "$1"`; do - hasSUID "$W" && echo "$W" && alias kubectl="$W" && return 0 + T=/tmp/$1 + for W in "$T" `which "$1"`; do + hasSUID "$W" && echo "$W" && return 0 done R=`realpath $W` || return 1 - echo "$R" sudo -n true >/dev/null 2>&1 || log "It's necessary to provide sudo password to run '$1' as root" sudo -B true || return 1 - sudo rm -f /tmp/$1 runCmd "$TEST" "Changing owner to root to: $R" "sudo chown root $R" \ - && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $R" && return 0 + && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $R" && echo "kubectl" && return 0 - runCmd "$TEST" "Coping $R" "sudo cp $R /tmp/$1" \ - && runCmd "$TEST" "Changing owner to root to: /tmp/$1" "sudo chown root /tmp/$1" \ - && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s /tmp/$1" && return 0 + runCmd "$TEST" "Coping $R" "sudo cp $R $T" \ + && runCmd "$TEST" "Changing owner to root to: $T" "sudo chown root $T" \ + && runCmd "$TEST" "Changing set-uid to: $R" "sudo chmod u+s $T" && echo "$T" && return 0 } ## @@ -58,13 +57,12 @@ startPortForward() { H=`getPids "kubectl port-forward $2"` [ -n "$H" ] && log "Already running k8s port-forward $1 $2 $3 -> $4 with pid $H" && return 0 [ -z "$TEST" ] && log "Starting k8s port-forward $1 $2 $3 -> $4" - [ "$4" -le 1024 ] && setSuid kubectl || return 1 + [ "$4" -le 1024 ] && K=`setSuid kubectl` || return 1 bgf="k8s-port-forward-$3-$4.log" rm -f "$bgf" - [ -x /tmp/kubectl ] && K=/tmp/kubectl || K=kubectl runInBackgroundToFile "$K port-forward $2 $4:$3 -n $1" "$bgf" sleep 2 - cat "$bgf" | egrep 'Forwarding from' + egrep 'Forwarding from' "$bgf" } ## diff --git a/scripts/pit/lib/lib-playwright.sh b/scripts/pit/lib/lib-playwright.sh index 17a4566c..e70b767c 100644 --- a/scripts/pit/lib/lib-playwright.sh +++ b/scripts/pit/lib/lib-playwright.sh @@ -13,8 +13,7 @@ isInstalledPlaywright() { installPlaywright() { _pfile="playwright-"`uname`".out" _dir=`dirname "$1"` - # @playwright/test - (cd "$_dir" && runToFile "'${NPM}' install --no-audit @playwright/test" "$_pfile" "$VERBOSE") || return 1 + (cd "$_dir" && runToFile "$NPM install --no-audit @playwright/test" "$_pfile" "$VERBOSE") || return 1 (cd "$_dir" && runToFile "npx playwright install chromium" "$_pfile" "$VERBOSE") || return 1 isLinux && (cd "$_dir" && runToFile "'${NODE}' ./node_modules/.bin/playwright install-deps chromium" "$_pfile" "$VERBOSE") || true } @@ -44,10 +43,10 @@ runPlaywrightTests() { PATH=$PATH runToFile "'$NODE' '$_test_file' $_args" "$_pfile" "$VERBOSE" true err=$? [ -n "$TEST" ] && return 0 - H=`grep '> CONSOLE:' "$_pfile" | perl -pe 's/(> CONSOLE: Received xhr.*?feat":).*/$1 .../g'` + H=`grep ' > CONSOLE:' "$_pfile" | perl -pe 's/(> CONSOLE: Received xhr.*?feat":).*/$1 .../g'` H=`echo "$H" | egrep -v 'Atmosphere|Vaadin push loaded|Websocket successfully opened|Websocket closed|404.*favicon.ico'` [ -n "$H" ] && [ "$_mode" = "prod" ] && reportError "Console Warnings in $_mode mode $5" "$H" && echo "$H" - H=`grep '> JSERROR:' "$_pfile"` + H=`grep ' > JSERROR:' "$_pfile"` [ -n "$H" ] && reportError "Console Errors in $_msg" "$H" && echo "$H" && return 1 H=`tail -15 $_pfile` [ $err != 0 ] && reportOutErrors "$_ofile" "Error ($err) running Visual-Test ("`basename $_pfile`")" || echo ">>>> PiT: playwright '$_test_file' done" >> $__file diff --git a/scripts/pit/lib/lib-utils.sh b/scripts/pit/lib/lib-utils.sh index c7338d55..28fd26c5 100644 --- a/scripts/pit/lib/lib-utils.sh +++ b/scripts/pit/lib/lib-utils.sh @@ -167,7 +167,7 @@ computeNpm() { NPM=`which npm` NPX=`which npx` NODE=`which node` - [ -x "$_VNODE/bin/node" -a -f "$_NPMJS" ] && export PATH="$_VNODE/bin:$PATH" && NODE="$_VNODE/bin/node" && NPM="'$NODE' $_NPMJS" + [ -x "$_VNODE/bin/node" -a -f "$_NPMJS" ] && export PATH="$_VNODE/bin:$PATH" && NODE="$_VNODE/bin/node" && NPM="'$NODE' '$_NPMJS'" } ## Run a command, and shows a message explaining it @@ -717,7 +717,7 @@ NODE=$NODE Java version: `java -version 2>&1` Node version: `"$NODE" --version` NPM=$NPM -Npm version: `"$NPM" --version` +Npm version: `eval $NPM --version` " } From c8ab707c0d107b3b6a54d8d480944d23aacff0fa Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 17:33:27 +0100 Subject: [PATCH 11/36] more logs in case of failure --- scripts/pit/run.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/pit/run.sh b/scripts/pit/run.sh index cbb8c33f..f440b21c 100755 --- a/scripts/pit/run.sh +++ b/scripts/pit/run.sh @@ -104,6 +104,10 @@ main() { if [ $i = control-center ]; then mkdir -p tmp/$i && cd tmp/$i run runControlCenter $i + if [ $? != 0 ]; then + kubectl get pods -n $CC_NS + ps -feaww | grep kubectl + fi cd "$pwd" continue elif expr "$i" : '.*_jdk' >/dev/null; then From 6eeb25164db605ee76e43a1daeaae27601f5e265 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 17:49:24 +0100 Subject: [PATCH 12/36] ignore https errors --- scripts/pit/its/cc-identity-management.js | 2 +- scripts/pit/its/cc-localization.js | 2 +- scripts/pit/its/test-utils.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index bb7d0e1e..0f4ab553 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -80,7 +80,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await takeScreenshot(page, __filename, 'user-created'); log(`Logging in ${app} as ${user} ...\n`); - pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); + pageApp = await createPage(arg.headless, true); await waitForServerReady(pageApp, url); await takeScreenshot(pageApp, __filename, `app-${app}-loaded`); await pageApp.getByLabel('Email').fill(user); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 0eed5b43..a2c33b39 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -37,7 +37,7 @@ const { assert } = require('console'); log(`Checking that ${app} installed in ${url} is running ...\n`); // When app is not running, localization cannot be enabled - const pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); + const pageApp = await createPage(arg.headless, true); await waitForServerReady(pageApp, url); await closePage(pageApp); await takeScreenshot(page, __filename, 'app-running'); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index ec1b9ebe..b954894c 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -96,7 +96,7 @@ async function waitForServerReady(page, url, options = {}) { log(`Opening ${url}\n`); for (let attempt = 0; attempt < maxRetries; attempt++) { try { - const response = await page.goto(url, {timeou: 120000}); + const response = await page.goto(url, {timeout: 120000}); // Check if the response status is not 503 if (response && response.status() < 400) { out(` ⏲ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); From 515d294cb29f2908b5223b7ca7efb6ba8cc3b6a5 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 17:52:29 +0100 Subject: [PATCH 13/36] increase viewport --- scripts/pit/its/test-utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index b954894c..acaa4c23 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -61,9 +61,9 @@ async function createPage(headless, ignoreHTTPSErrors) { const browser = await chromium.launch({ headless: headless, chromiumSandbox: false, - slowMo: 500 + slowMo: 500, }); - const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors }); + const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors, viewport: { width: 1920, height: 1080 } }); const page = await context.newPage(); page.on('console', msg => out("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '), '\n')); page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); From 95faf23dd16accdbae7e684ba21bd435962d763c Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 17:58:42 +0100 Subject: [PATCH 14/36] check TLs for apps --- scripts/pit/lib/lib-ccenter.sh | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 77b0fdfb..78a9833a 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -76,15 +76,19 @@ uninstallCC() { runCmd "$TEST" "Removing namespace $CC_NS" kubectl delete ns $CC_NS $KD } +getTLs() { + log "TLS config for ingress: $1" + H=`kubectl get ingress $1 -n $CC_NS -o jsonpath='{.spec.rules[0].host}'` + HS=`kubectl get ingress $1 -n $CC_NS -o jsonpath='{.spec.tls[*].hosts[*]}'` + S=`kubectl get ingress $1 -n $CC_NS -o jsonpath='{.spec.tls[*].secretName}'` + C=`kubectl get secret $S -n $CC_NS -o go-template='{{ index .data "tls.crt" | base64decode }}' | openssl x509 -noout -issuer -subject -enddate | tr '\n' ' '` + log "Host: $H is in ingress $1 with TLS config\n hosts: $HS secret: $S cert: $C" +} + checkTls() { [ -n "$TEST" -o -n "$SKIPHELM" ] && return 0 for i in `kubectl get ingresses -n $CC_NS | grep nginx | awk '{print $1}'`; do - log "$i" - H=`kubectl get ingress $i -n $CC_NS -o jsonpath='{.spec.rules[0].host}'` - HS=`kubectl get ingress $i -n $CC_NS -o jsonpath='{.spec.tls[*].hosts[*]}'` - S=`kubectl get ingress $i -n $CC_NS -o jsonpath='{.spec.tls[*].secretName}'` - C=`kubectl get secret $S -n $CC_NS -o go-template='{{ index .data "tls.crt" | base64decode }}' | openssl x509 -noout -issuer -subject -enddate | tr '\n' ' '` - log "Host: $H is in ingress $i with TLS config\n hosts: $HS secret: $S cert: $C" + getTLs "$i" done } @@ -140,6 +144,7 @@ runPwTests() { [ -z "$CC_CERT" -o -z "$CC_KEY" ] && NO_TLS=--notls || NO_TLS="" for f in $CC_TESTS; do runPlaywrightTests "$PIT_SCR_FOLDER/its/$f" "" "prod" "control-center" --url=https://$CC_CONTROL --login=$CC_EMAIL $NO_TLS || return 1 + [ "$f" = cc-install-apps.js ] && checkTls sleep 3 done } From b667af6fbb3e57d051af7b0e103bc697545c5c97 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 18:02:17 +0100 Subject: [PATCH 15/36] log about certs for apps --- scripts/pit/its/cc-install-apps.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/pit/its/cc-install-apps.js b/scripts/pit/its/cc-install-apps.js index 0c0d0a08..b4e3b3a7 100644 --- a/scripts/pit/its/cc-install-apps.js +++ b/scripts/pit/its/cc-install-apps.js @@ -1,6 +1,6 @@ const { expect} = require('@playwright/test'); const fs = require('fs'); -const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = require('./test-utils'); +const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, run} = require('./test-utils'); const arg = args(); let count = 0; @@ -22,6 +22,7 @@ async function installApp(app, page) { await page.getByLabel('Image', {exact: true}).fill(`k8sdemos/${app}:latest`) await page.getByLabel('Application URI', {exact: true}).locator('input[type="text"]').fill(uri) if (cert) { + log(`Uploading certificate ${cert} for ${app} ...\n`); await page.getByLabel('Upload').click(); const fileChooserPromise = page.waitForEvent('filechooser'); await page.getByText('Browse').click(); @@ -29,6 +30,9 @@ async function installApp(app, page) { await fileChooser.setFiles(cert); fileChooserPromise.then(await page.locator('.detail-layout').getByRole('button', {name: 'Deploy'}).click()) } else { + log(`No certificate found for ${app}\n`); + run(`pwd`); + run(`ls -l`); await page.getByLabel('Generate').click(); await page.locator('.detail-layout').getByRole('button', {name: 'Deploy'}).click(); } From 5b1b021321e9cbb4cd2debbc19636fed5376cfcb Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 19:16:05 +0100 Subject: [PATCH 16/36] more reliable upload await --- scripts/pit/its/cc-install-apps.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/pit/its/cc-install-apps.js b/scripts/pit/its/cc-install-apps.js index b4e3b3a7..1541b52c 100644 --- a/scripts/pit/its/cc-install-apps.js +++ b/scripts/pit/its/cc-install-apps.js @@ -1,6 +1,6 @@ const { expect} = require('@playwright/test'); const fs = require('fs'); -const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, run} = require('./test-utils'); +const {log, args, run, createPage, closePage, takeScreenshot, waitForServerReady} = require('./test-utils'); const arg = args(); let count = 0; @@ -22,22 +22,25 @@ async function installApp(app, page) { await page.getByLabel('Image', {exact: true}).fill(`k8sdemos/${app}:latest`) await page.getByLabel('Application URI', {exact: true}).locator('input[type="text"]').fill(uri) if (cert) { - log(`Uploading certificate ${cert} for ${app} ...\n`); + log(`Uploading certificate ${cert} for ${app}...\n`); await page.getByLabel('Upload').click(); const fileChooserPromise = page.waitForEvent('filechooser'); await page.getByText('Browse').click(); const fileChooser = await fileChooserPromise; await fileChooser.setFiles(cert); - fileChooserPromise.then(await page.locator('.detail-layout').getByRole('button', {name: 'Deploy'}).click()) + await takeScreenshot(page, __filename, `form-filled-${app}`); + await page.locator('.detail-layout').getByRole('button', {name: 'Deploy'}).click(); } else { + log(`No certificate found for ${app}...\n`); log(`No certificate found for ${app}\n`); run(`pwd`); run(`ls -l`); await page.getByLabel('Generate').click(); + await takeScreenshot(page, __filename, `form-filled-${app}`); await page.locator('.detail-layout').getByRole('button', {name: 'Deploy'}).click(); } - await takeScreenshot(page, __filename, `form-filled-${app}`); + await takeScreenshot(page, __filename, `form-clicked-${app}`); await page.getByRole('listitem').filter({ hasText: 'Settings'}).click() From 8ad5ff5417ec2f7f493c48702125f7a940b05069 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 29 Jan 2025 21:49:36 +0100 Subject: [PATCH 17/36] add --cluster= parameter in order to run tests in any cluster --- scripts/pit/its/cc-identity-management.js | 3 ++ scripts/pit/its/test-utils.js | 5 ++- scripts/pit/lib/lib-args.sh | 4 +- scripts/pit/lib/lib-ccenter.sh | 48 ++++++++++++++++------- scripts/pit/lib/lib-k8s-kind.sh | 27 ++----------- 5 files changed, 45 insertions(+), 42 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 0f4ab553..ffc225f5 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -50,6 +50,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r log(`Adding Role, Group and User ...\n`); await page.getByRole('link', { name: 'Roles' }).click(); + await page.waitForTimeout(2000); await page.getByRole('button', { name: 'Create' }).click(); await takeScreenshot(page, __filename, 'role-form'); await page.getByLabel('Name').fill(role); @@ -59,6 +60,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await takeScreenshot(page, __filename, 'role-created'); await page.getByRole('link', { name: 'Groups' }).click(); + await page.waitForTimeout(2000); await page.getByRole('button', { name: 'Create' }).click(); await takeScreenshot(page, __filename, 'group-form'); await page.getByLabel('Name').fill(group); @@ -68,6 +70,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await takeScreenshot(page, __filename, 'group-created'); await page.getByRole('link', { name: 'Users' }).click(); + await page.waitForTimeout(2000); await page.getByRole('button', { name: 'Create' }).click(); await takeScreenshot(page, __filename, 'user-form'); await page.getByLabel('First Name').fill(role); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index acaa4c23..a817fe47 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -61,9 +61,10 @@ async function createPage(headless, ignoreHTTPSErrors) { const browser = await chromium.launch({ headless: headless, chromiumSandbox: false, - slowMo: 500, + slowMo: headless ? -1: 500, + args: ['--window-position=0,0'] }); - const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors, viewport: { width: 1920, height: 1080 } }); + const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors, viewport: { width: 1792, height: 970 } }); const page = await context.newPage(); page.on('console', msg => out("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '), '\n')); page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); diff --git a/scripts/pit/lib/lib-args.sh b/scripts/pit/lib/lib-args.sh index ec0732a2..c2e4bab8 100644 --- a/scripts/pit/lib/lib-args.sh +++ b/scripts/pit/lib/lib-args.sh @@ -23,6 +23,7 @@ Use: $0 with the next options: --skip-clean Do not clean maven cache --skip-helm Do not re-install control-center with helm and continue running tests --skip-pw Do not run playwright tests + --cluster=name Run tests in an existing k8s cluster --keep-cc Keep control-center running after tests --pnpm Use pnpm instead of npm to speed up frontend compilation (default npm) --vite Use vite inetad of webpack to speed up frontend compilation (default webpack) @@ -37,7 +38,7 @@ Use: $0 with the next options: everything after this argument is the function name and arguments passed to the function. you should take care with arguments that contain spaces, they should be quoted twice. --help Show this message - --starters=list List of demos or presets separated by comma to run (default: all) valid options:`echo ,$DEFAULT_STARTERS | sed -e 's/,/\n · /g'` + --starters=list List of demos or presets separated by comma to run (default: all) valid options:`echo ,$DEFAULT_STARTERS | tr ' ' , | sed -e 's/,/\n · /g'` EOF exit 1 } @@ -74,6 +75,7 @@ checkArgs() { --skip-dev) NODEV=true;; --skip-prod) NOPROD=true;; --skip-pw) SKIPPW=true;; + --cluster=*) CLUSTER="$arg";; --skip-helm) SKIPHELM=true;; --keep-cc) KEEPCC=true;; --pnpm) PNPM="-Dpnpm.enable=true";; diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 78a9833a..23fb7fd3 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -92,11 +92,17 @@ checkTls() { done } +reloadIngress() { + [ -n "$TEST" ] && return 0 + pod=`kubectl -n $CC_NS get pods | grep control-center-ingress-nginx-controller | awk '{print $1}'` || return 1 + [ -n "$pod" ] && runCmd "$TEST" "Reloading nginx in $pod" "kubectl exec $pod -n "$CC_NS" -- nginx -s reload" || return 1 +} + ## Configure secrets for the control-center and the keycloak servers installTls() { [ -n "$SKIPHELM" ] && return 0 [ -z "$CC_KEY" -o -z "$CC_CERT" ] && log "No CC_KEY and CC_CERT provided, skiping TLS installation" && return 0 - [ -n "$CC_FULL" ] && CC_CERT=$CC_FULL + [ -n "$CC_FULL" ] && CC_CERT="$CC_FULL" [ -z "$TEST" ] && log "Installing TLS $CC_TLS for $CC_CONTROL and $CC_AUT" || cmd "## Creating TLS file '$CC_DOMAIN.pem' from envs" f1=cc-tls.crt f2=cc-tls.key @@ -124,8 +130,7 @@ installTls() { [ -n "$TEST" ] && return 0 - pod=`kubectl -n $CC_NS get pods | grep control-center-ingress-nginx-controller | awk '{print $1}'` || return 1 - [ -n "$pod" ] && runCmd "$TEST" "Reloading nginx in $pod" "kubectl exec $pod -n "$CC_NS" -- nginx -s reload" || return 1 + reloadIngress || return 1 } ## Show temporary user-email and password in the terminal @@ -144,38 +149,51 @@ runPwTests() { [ -z "$CC_CERT" -o -z "$CC_KEY" ] && NO_TLS=--notls || NO_TLS="" for f in $CC_TESTS; do runPlaywrightTests "$PIT_SCR_FOLDER/its/$f" "" "prod" "control-center" --url=https://$CC_CONTROL --login=$CC_EMAIL $NO_TLS || return 1 - [ "$f" = cc-install-apps.js ] && checkTls + [ "$f" = cc-install-apps.js ] && reloadIngress && checkTls sleep 3 done } ## Main method for running control center runControlCenter() { - checkCommands kind helm docker kubectl || return 1 - checkBusyPort "443" || return 1 + CLUSTER=${CLUSTER:-$CC_CLUSTER} + + checkCommands docker kubectl helm || return 1 checkDockerRunning || return 1 - ## Clean up from a previous run - [ -z "$SKIPHELM" ] && uninstallCC $CC_CLUSTER $CC_NS - # deleteCluster $CC_CLUSTER - ## Start a new cluster - createCluster $CC_CLUSTER $CC_NS || return 1 - # startCloudProvider || return 1 + + ## Start a new kind cluster if needed + [ "$CLUSTER" != "$CC_CLUSTER" ] || createKindCluster $CC_CLUSTER $CC_NS || return 1 + + ## Set the context to the cluster + kubectl config set-context $CLUSTER || return 1 + kubectl config set-context --current --namespace=$CC_NS || return 1 + kubectl get ns >/dev/null 2>&1 || return 1 + + ## Clean up CC from a previous run unless SKIPHELM is set + [ -z "$SKIPHELM" ] && uninstallCC $CLUSTER $CC_NS + ## Install Control Center installCC || return 1 ## Control center takes a long time to start waitForCC 900 || return 1 + ## Show temporary user-email and password in the terminal showTemporaryPassword + ## Install TLS certificates for the control-center and keycloak installTls && checkTls || return 1 + ## Forward the ingress (it needs root access since it uses port 443) - forwardIngress $CC_NS || return 1 - ## Run Playwright tests + # checkBusyPort "443" + checkPort 443 || forwardIngress $CC_NS || return 1 + + ## Run Playwright tests for the control-center runPwTests || return 1 if [ -z "$TEST" -a -z "$KEEPCC" ]; then stopForwardIngress || return 1 - deleteCluster $CC_CLUSTER || return 1 + [ "$CLUSTER" != "$CC_CLUSTER" ] || deleteKindCluster $CC_CLUSTER || return 1 fi + return 0 } diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index 7ecd9617..99a995e2 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -1,22 +1,5 @@ . `dirname $0`/lib/lib-utils.sh -startCloudProvider() { - [ -z "$TEST" ] && docker container inspect kind-cloud-provider >/dev/null 2>&1 && log "Docker Kind Cloud Provider already running" && return - runCmd "$TEST" "Starting Docker KinD Cloud Provider" \ - "docker run --quiet --name kind-cloud-provider --rm -d \ - -v /var/run/docker.sock:/var/run/docker.sock \ - rophy/cloud-provider-kind:0.4.0-20241026-r1" - - # --network kind -p 443:443 -} - -stopCloudProvider() { - docker ps | grep kind-cloud-provider || return 0 - runCmd "$TEST" "Stoping Docker KinD Cloud Provider" \ - "docker kill kind-cloud-provider" || return 1 - docker ps | grep envoyproxy/envoy | awk '{print $1}' | xargs docker kill 2>/dev/null -} - ## Check that the command has SUID bit set # $1: command hasSUID() { @@ -84,7 +67,8 @@ stopForwardIngress() { ## # $1: cluster name # $2: namespace -createCluster() { +createKindCluster() { + checkCommands kind || return 1 kind get clusters | grep -q "^$1$" && return 0 runCmd "$TEST" "Creating KinD cluster: $1" \ "kind create cluster --name $1" || return 1 @@ -94,15 +78,10 @@ createCluster() { ## # $1: cluster name -deleteCluster() { +deleteKindCluster() { kind get clusters | grep -q "^$1$" || return 0 runCmd "$TEST" "Deleting Cluster $1" \ "kind delete cluster --name $1" || return 1 } - - - - - From 7ce32471be5032019eeb76122e72323fe4709fd9 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 07:44:07 +0100 Subject: [PATCH 18/36] increasing time --- scripts/pit/its/cc-install-apps.js | 14 ++++++++++---- scripts/pit/lib/lib-ccenter.sh | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/scripts/pit/its/cc-install-apps.js b/scripts/pit/its/cc-install-apps.js index 1541b52c..7a1c5b34 100644 --- a/scripts/pit/its/cc-install-apps.js +++ b/scripts/pit/its/cc-install-apps.js @@ -73,11 +73,17 @@ async function installApp(app, page) { log(`Waiting for 2 applications to be available...\n`); const selector = 'vaadin-grid-cell-content span[theme="badge success"]'; - await expect(page.locator(selector).nth(0)).toBeVisible({ timeout: 180000 }); + const startTime = Date.now(); + + await expect(page.locator(selector).nth(0)).toBeVisible({ timeout: 280000 }); + const firstAppTime = (Date.now() - startTime) / 1000; await takeScreenshot(page, __filename, 'app-1-available'); - log(`First application is available\n`); - await expect(page.locator(selector).nth(1)).toBeVisible({ timeout: 180000 }); + log(`First application is available after ${firstAppTime.toFixed(2)} seconds\n`); + + await expect(page.locator(selector).nth(1)).toBeVisible({ timeout: 280000 }); + const secondAppTime = (Date.now() - startTime) / 1000; await takeScreenshot(page, __filename, 'app-2-available'); - log(`Second application is available\n`); + log(`Second application is available after ${secondAppTime.toFixed(2)} seconds\n`); + await closePage(page); })(); diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 23fb7fd3..a6048d4a 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -149,7 +149,7 @@ runPwTests() { [ -z "$CC_CERT" -o -z "$CC_KEY" ] && NO_TLS=--notls || NO_TLS="" for f in $CC_TESTS; do runPlaywrightTests "$PIT_SCR_FOLDER/its/$f" "" "prod" "control-center" --url=https://$CC_CONTROL --login=$CC_EMAIL $NO_TLS || return 1 - [ "$f" = cc-install-apps.js ] && reloadIngress && checkTls + [ "$f" = cc-install-apps.js ] && reloadIngress && ls -l && cat *.pem && checkTls sleep 3 done } From fa48fc6f70bcc1a6632a5a780a762bbaa254e4c4 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 07:50:41 +0100 Subject: [PATCH 19/36] Do not fail in clean up --- scripts/pit/its/cc-identity-management.js | 41 ++++++++++++----------- scripts/pit/its/cc-localization.js | 17 ++++++---- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index ffc225f5..12e45c8e 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -1,5 +1,5 @@ const { expect} = require('@playwright/test'); -const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = require('./test-utils'); +const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, err} = require('./test-utils'); (async () => { const arg = args(); @@ -94,23 +94,26 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady} = r await closePage(pageApp); log('Cleaning up...\n'); - await page.getByRole('link', { name: 'Roles' }).click(); - await page.getByText('admin').nth(1).click(); - await page.getByRole('button', { name: 'Delete' }).click(); - await page.getByRole('button', { name: 'Delete' }).nth(0).click(); - await page.getByRole('link', { name: 'Groups' }).click(); - await page.getByText('admin', { exact: true }).click(); - await page.getByRole('button', { name: 'Delete' }).click(); - await page.getByRole('button', { name: 'Delete' }).nth(0).click(); - await page.getByRole('link', { name: 'Users' }).click(); - await page.getByText('admin user').click(); - await page.getByRole('button', { name: 'Delete' }).click(); - await page.getByRole('button', { name: 'Delete' }).nth(0).click(); - await page.getByRole('link', { name: 'Settings' }).click(); - await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); - await page.getByLabel('Identity Management').uncheck(); - await page.getByRole('button', { name: 'Disable' }).click(); - await page.getByRole('button', { name: 'Update' }).click(); - + try { + await page.getByRole('link', { name: 'Roles' }).click(); + await page.getByText('admin').nth(1).click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.getByRole('link', { name: 'Groups' }).click(); + await page.getByText('admin', { exact: true }).click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.getByRole('link', { name: 'Users' }).click(); + await page.getByText('admin user').click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.getByRole('link', { name: 'Settings' }).click(); + await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); + await page.getByLabel('Identity Management').uncheck(); + await page.getByRole('button', { name: 'Disable' }).click(); + await page.getByRole('button', { name: 'Update' }).click(); + } catch (error) { + err(`Error cleaning up: ${error}\n`); + } await closePage(page); })(); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index a2c33b39..9261cabf 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -99,12 +99,15 @@ const { assert } = require('console'); await closePage(pagePrev); log('Cleaning up...\n'); - await page.getByRole('button', { name: 'Stop preview' }).click(); - await page.getByRole('link', { name: 'Settings' }).click(); - await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); - await page.getByLabel('Localization').uncheck(); - await page.getByRole('button', { name: 'Disable' }).click(); - await page.getByRole('button', { name: 'Update' }).click(); - + try { + await page.getByRole('button', { name: 'Stop preview' }).click(); + await page.getByRole('link', { name: 'Settings' }).click(); + await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); + await page.getByLabel('Localization').uncheck(); + await page.getByRole('button', { name: 'Disable' }).click(); + await page.getByRole('button', { name: 'Update' }).click(); + } catch (error) { + err(`Error cleaning up: ${error}\n`); + } await closePage(page); })(); From 793b470d8adc90125cb5d12f9e4d3cb29f5700f3 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 08:11:04 +0100 Subject: [PATCH 20/36] fix screenshots --- scripts/pit/its/cc-identity-management.js | 3 ++- scripts/pit/its/cc-localization.js | 7 ++++--- scripts/pit/lib/lib-ccenter.sh | 2 +- scripts/pit/lib/lib-k8s-kind.sh | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 12e45c8e..397fdd0e 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -36,8 +36,8 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, err // When app is not running, localization cannot be enabled let pageApp = await createPage(arg.headless, true); await waitForServerReady(pageApp, url); + await takeScreenshot(pageApp, __filename, 'app-running'); await closePage(pageApp); - await takeScreenshot(page, __filename, 'app-running'); log(`Enabling identity Management ...\n`); await page.locator('vaadin-select vaadin-input-container div').click(); @@ -114,6 +114,7 @@ const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, err await page.getByRole('button', { name: 'Update' }).click(); } catch (error) { err(`Error cleaning up: ${error}\n`); + await takeScreenshot(page, __filename, 'error-cleaning'); } await closePage(page); })(); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 9261cabf..a7311b9e 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -39,8 +39,8 @@ const { assert } = require('console'); // When app is not running, localization cannot be enabled const pageApp = await createPage(arg.headless, true); await waitForServerReady(pageApp, url); + await takeScreenshot(pageApp, __filename, 'app-running'); await closePage(pageApp); - await takeScreenshot(page, __filename, 'app-running'); log(`Uploading and updating localization keys ...\n`); await page.locator('vaadin-select vaadin-input-container div').click(); @@ -85,7 +85,7 @@ const { assert } = require('console'); await page.getByRole('button', { name: 'Start preview' }).click(); const pagePrev = await createPage(arg.headless, true); await waitForServerReady(pagePrev, previewUrl); - await takeScreenshot(page, __filename, 'preview-ready'); + await takeScreenshot(pagePrev, __filename, 'preview-ready'); const text = await pagePrev.getByText(/Password|Dashboard/).textContent(); if (text.includes('Password')) { await pagePrev.getByLabel('Email').fill(user); @@ -93,7 +93,7 @@ const { assert } = require('console'); await pagePrev.getByRole('button', {name: 'Sign In'}).click() await takeScreenshot(pagePrev, __filename, 'preview-logged-in'); await expect(pagePrev.getByRole('button', { name: 'New order' })).toBeVisible(); - await takeScreenshot(page, __filename, 'preview-loaded'); + await takeScreenshot(pagePrev, __filename, 'preview-loaded'); } // await expect(pagePrev.getByText('Panaderia', { exact: true })).toBeVisible(); await closePage(pagePrev); @@ -108,6 +108,7 @@ const { assert } = require('console'); await page.getByRole('button', { name: 'Update' }).click(); } catch (error) { err(`Error cleaning up: ${error}\n`); + await takeScreenshot(page, __filename, 'error-cleaning'); } await closePage(page); })(); diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index a6048d4a..fef08216 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -102,7 +102,7 @@ reloadIngress() { installTls() { [ -n "$SKIPHELM" ] && return 0 [ -z "$CC_KEY" -o -z "$CC_CERT" ] && log "No CC_KEY and CC_CERT provided, skiping TLS installation" && return 0 - [ -n "$CC_FULL" ] && CC_CERT="$CC_FULL" + # [ -n "$CC_FULL" ] && CC_CERT="$CC_FULL" [ -z "$TEST" ] && log "Installing TLS $CC_TLS for $CC_CONTROL and $CC_AUT" || cmd "## Creating TLS file '$CC_DOMAIN.pem' from envs" f1=cc-tls.crt f2=cc-tls.key diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index 99a995e2..ea135084 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -74,7 +74,7 @@ createKindCluster() { "kind create cluster --name $1" || return 1 runCmd "$TEST" "Setting default namespace $2" \ "kubectl config set-context --current --namespace=$2" -} + } ## # $1: cluster name From b6928c2e9fa919befd5cbad0d8e384b7d710c49c Mon Sep 17 00:00:00 2001 From: edler-san <19165931+edler-san@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:40:52 +0100 Subject: [PATCH 21/36] Adjust timeout for windows --- scripts/pit/its/test-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index a817fe47..0ce29c53 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -82,7 +82,7 @@ async function takeScreenshot(page, name, descr) { const scr = path.basename(name); const cnt = String(++sscount).padStart(2, "0"); const file = `${screenshots}/${scr}-${cnt}-${descr}.png`; - await page.waitForTimeout(1000); + await page.waitForTimeout(10000); await page.screenshot({ path: file }); out(` 📸 Screenshot taken: ${file}\n`); } From 9b75ce80e1c5076418b51883b412ad0586c244b7 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 11:06:15 +0100 Subject: [PATCH 22/36] fix imports --- scripts/pit/its/cc-identity-management.js | 2 +- scripts/pit/its/cc-localization.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 397fdd0e..e3c43996 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -1,5 +1,5 @@ const { expect} = require('@playwright/test'); -const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, err} = require('./test-utils'); +const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady} = require('./test-utils'); (async () => { const arg = args(); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index a7311b9e..93bb52d8 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -1,7 +1,6 @@ const { expect} = require('@playwright/test'); const fs = require('fs'); -const path = require('path'); -const {log, args, createPage, closePage, takeScreenshot, waitForServerReady, run} = require('./test-utils'); +const {log, err, args, run, createPage, closePage, takeScreenshot, waitForServerReady} = require('./test-utils'); const { assert } = require('console'); (async () => { From f8973df724a3e5a17eeb17234d02f3e8eb114347 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 12:42:03 +0100 Subject: [PATCH 23/36] fix issues when cleaning up and certs --- scripts/pit/its/cc-identity-management.js | 16 ++++++++++------ scripts/pit/its/test-utils.js | 6 +++--- scripts/pit/lib/lib-ccenter.sh | 5 +++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index e3c43996..7265a1f5 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -96,18 +96,22 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady log('Cleaning up...\n'); try { await page.getByRole('link', { name: 'Roles' }).click(); - await page.getByText('admin').nth(1).click(); + await page.waitForTimeout(2000); + await page.getByText(role, { exact: true }).nth(1).click(); await page.getByRole('button', { name: 'Delete' }).click(); - await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.locator('vaadin-confirm-dialog-overlay').getByRole('button', { name: 'Delete' }).click(); await page.getByRole('link', { name: 'Groups' }).click(); - await page.getByText('admin', { exact: true }).click(); + await page.waitForTimeout(2000); + await page.getByText(group, { exact: true }).click(); await page.getByRole('button', { name: 'Delete' }).click(); - await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.locator('vaadin-confirm-dialog-overlay').getByRole('button', { name: 'Delete' }).click(); await page.getByRole('link', { name: 'Users' }).click(); - await page.getByText('admin user').click(); + await page.waitForTimeout(2000); + await page.getByText(user, { exact: true }).click(); await page.getByRole('button', { name: 'Delete' }).click(); - await page.getByRole('button', { name: 'Delete' }).nth(0).click(); + await page.locator('vaadin-confirm-dialog-overlay').getByRole('button', { name: 'Delete' }).click(); await page.getByRole('link', { name: 'Settings' }).click(); + await page.waitForTimeout(2000); await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); await page.getByLabel('Identity Management').uncheck(); await page.getByRole('button', { name: 'Disable' }).click(); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index 0ce29c53..bb7f910c 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -61,12 +61,11 @@ async function createPage(headless, ignoreHTTPSErrors) { const browser = await chromium.launch({ headless: headless, chromiumSandbox: false, - slowMo: headless ? -1: 500, args: ['--window-position=0,0'] }); const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors, viewport: { width: 1792, height: 970 } }); const page = await context.newPage(); - page.on('console', msg => out("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '), '\n')); + page.on('console', msg => /vaadinPush/.test(msg) || out("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '), '\n')); page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); page.browser = browser; return page; @@ -82,7 +81,7 @@ async function takeScreenshot(page, name, descr) { const scr = path.basename(name); const cnt = String(++sscount).padStart(2, "0"); const file = `${screenshots}/${scr}-${cnt}-${descr}.png`; - await page.waitForTimeout(10000); + await page.waitForTimeout(/^win/.test(process.platform) ? 10000: 1500); await page.screenshot({ path: file }); out(` 📸 Screenshot taken: ${file}\n`); } @@ -101,6 +100,7 @@ async function waitForServerReady(page, url, options = {}) { // Check if the response status is not 503 if (response && response.status() < 400) { out(` ⏲ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); + page.waitForTimeout(1500); return response; } else { out(` ⏲ Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index fef08216..442887ae 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -103,12 +103,13 @@ installTls() { [ -n "$SKIPHELM" ] && return 0 [ -z "$CC_KEY" -o -z "$CC_CERT" ] && log "No CC_KEY and CC_CERT provided, skiping TLS installation" && return 0 # [ -n "$CC_FULL" ] && CC_CERT="$CC_FULL" - [ -z "$TEST" ] && log "Installing TLS $CC_TLS for $CC_CONTROL and $CC_AUT" || cmd "## Creating TLS file '$CC_DOMAIN.pem' from envs" + [ -z "$TEST" ] && log "Installing TLS $CC_TLS for $CC_CONTROL and $CC_AUTH" || cmd "## Creating TLS file '$CC_DOMAIN.pem' from envs" f1=cc-tls.crt f2=cc-tls.key + f3=$CC_DOMAIN.pem echo -e "$CC_CERT" > $f1 || return 1 echo -e "$CC_KEY" > $f2 || return 1 - cat $f1 $f2 > $CC_DOMAIN.pem + cat $f1 $f2 > $f3 # remove old secrets if they exist (only needed for testing purposes since secrets are deleted before running the helm chart) kubectl get secret $CC_TLS_A -n $CC_NS >/dev/null 2>&1 && kubectl delete secret $CC_TLS_A -n $CC_NS From 35c28c238e3571b9ff600d4283f46e60cae8e6cf Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 13:08:06 +0100 Subject: [PATCH 24/36] improve console logs --- scripts/pit/its/cc-localization.js | 2 ++ scripts/pit/its/test-utils.js | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 93bb52d8..1e77b7a7 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -101,7 +101,9 @@ const { assert } = require('console'); try { await page.getByRole('button', { name: 'Stop preview' }).click(); await page.getByRole('link', { name: 'Settings' }).click(); + await page.waitForTimeout(2000); await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); + await page.waitForTimeout(2000); await page.getByLabel('Localization').uncheck(); await page.getByRole('button', { name: 'Disable' }).click(); await page.getByRole('button', { name: 'Update' }).click(); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index bb7f910c..f435a7e5 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -65,7 +65,10 @@ async function createPage(headless, ignoreHTTPSErrors) { }); const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors, viewport: { width: 1792, height: 970 } }); const page = await context.newPage(); - page.on('console', msg => /vaadinPush/.test(msg) || out("> CONSOLE:", (msg.text() + ' - ' + msg.location().url).replace(/\s+/g, ' '), '\n')); + page.on('console', msg => { + const text = `${msg.text()} - ${msg.location().url}`.replace(/\s+/g, ' '); + if (!/vaadinPush/.test(msg)) out("> CONSOLE:", text, '\n'); + }); page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); page.browser = browser; return page; @@ -94,6 +97,7 @@ async function waitForServerReady(page, url, options = {}) { } = options; log(`Opening ${url}\n`); + page.waitForTimeout(1000); for (let attempt = 0; attempt < maxRetries; attempt++) { try { const response = await page.goto(url, {timeout: 120000}); From fefaac5b982bd68b0e6521e5f32bef720b801863 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 13:35:19 +0100 Subject: [PATCH 25/36] clean left-overs logs in code --- scripts/pit/its/test-utils.js | 2 +- scripts/pit/lib/lib-ccenter.sh | 3 ++- scripts/pit/run.sh | 4 ---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index f435a7e5..994fcb41 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -67,7 +67,7 @@ async function createPage(headless, ignoreHTTPSErrors) { const page = await context.newPage(); page.on('console', msg => { const text = `${msg.text()} - ${msg.location().url}`.replace(/\s+/g, ' '); - if (!/vaadinPush/.test(msg)) out("> CONSOLE:", text, '\n'); + if (!/vaadinPush/.test(text)) out("> CONSOLE:", text, '\n'); }); page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); page.browser = browser; diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 442887ae..62a2197a 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -146,11 +146,12 @@ showTemporaryPassword() { ## Run Playwright tests for the control-center runPwTests() { computeNpm + [ -d screenshots.out ] && runCmd "$TEST" "Removing old screenshots" "rm -rf screenshots.out" [ -n "$SKIPPW" ] && return 0 [ -z "$CC_CERT" -o -z "$CC_KEY" ] && NO_TLS=--notls || NO_TLS="" for f in $CC_TESTS; do runPlaywrightTests "$PIT_SCR_FOLDER/its/$f" "" "prod" "control-center" --url=https://$CC_CONTROL --login=$CC_EMAIL $NO_TLS || return 1 - [ "$f" = cc-install-apps.js ] && reloadIngress && ls -l && cat *.pem && checkTls + [ "$f" = cc-install-apps.js ] && reloadIngress sleep 3 done } diff --git a/scripts/pit/run.sh b/scripts/pit/run.sh index f440b21c..cbb8c33f 100755 --- a/scripts/pit/run.sh +++ b/scripts/pit/run.sh @@ -104,10 +104,6 @@ main() { if [ $i = control-center ]; then mkdir -p tmp/$i && cd tmp/$i run runControlCenter $i - if [ $? != 0 ]; then - kubectl get pods -n $CC_NS - ps -feaww | grep kubectl - fi cd "$pwd" continue elif expr "$i" : '.*_jdk' >/dev/null; then From 0ee9132c1813fee7e8689d13943fb3495c282540 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 14:26:42 +0100 Subject: [PATCH 26/36] set cluster context, and check https in installed apps --- scripts/pit/its/cc-identity-management.js | 4 ++-- scripts/pit/its/cc-localization.js | 8 +++++--- scripts/pit/its/test-utils.js | 12 ++++++++---- scripts/pit/lib/lib-ccenter.sh | 19 +++++++++++++++---- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 7265a1f5..a9e5862b 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -34,7 +34,7 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady log(`Checking that ${app} installed in ${url} is running ...\n`); // When app is not running, localization cannot be enabled - let pageApp = await createPage(arg.headless, true); + let pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); await waitForServerReady(pageApp, url); await takeScreenshot(pageApp, __filename, 'app-running'); await closePage(pageApp); @@ -83,7 +83,7 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady await takeScreenshot(page, __filename, 'user-created'); log(`Logging in ${app} as ${user} ...\n`); - pageApp = await createPage(arg.headless, true); + pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); await waitForServerReady(pageApp, url); await takeScreenshot(pageApp, __filename, `app-${app}-loaded`); await pageApp.getByLabel('Email').fill(user); diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 1e77b7a7..376c1479 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -36,7 +36,7 @@ const { assert } = require('console'); log(`Checking that ${app} installed in ${url} is running ...\n`); // When app is not running, localization cannot be enabled - const pageApp = await createPage(arg.headless, true); + const pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); await waitForServerReady(pageApp, url); await takeScreenshot(pageApp, __filename, 'app-running'); await closePage(pageApp); @@ -82,7 +82,8 @@ const { assert } = require('console'); log(`Testing that preview page: ${previewUrl} is up and running\n`); await page.getByRole('button', { name: 'Start preview' }).click(); - const pagePrev = await createPage(arg.headless, true); + await page.waitForTimeout(5000); + const pagePrev = await createPage(arg.headless, true /* preview pages do not have a valid certificate */); await waitForServerReady(pagePrev, previewUrl); await takeScreenshot(pagePrev, __filename, 'preview-ready'); const text = await pagePrev.getByText(/Password|Dashboard/).textContent(); @@ -94,7 +95,8 @@ const { assert } = require('console'); await expect(pagePrev.getByRole('button', { name: 'New order' })).toBeVisible(); await takeScreenshot(pagePrev, __filename, 'preview-loaded'); } - // await expect(pagePrev.getByText('Panaderia', { exact: true })).toBeVisible(); + // TODO: bakery is not internationalized + // await expect(pagePrev.getByText('Panaderia', { exact: true })).toBeVisible(); await closePage(pagePrev); log('Cleaning up...\n'); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index 994fcb41..848f22b4 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -14,6 +14,10 @@ function log(...args) { function out(...args) { process.stderr.write(`\x1b[2m\x1b[196m${args}\x1b[0m`); } +function green(...args) { + process.stderr.write(`\x1b[2m\x1b[0;32m${args}\x1b[0m`); +} + function warn(...args) { process.stderr.write(`\x1b[2m\x1b[91m${args}\x1b[0m`); } @@ -67,7 +71,7 @@ async function createPage(headless, ignoreHTTPSErrors) { const page = await context.newPage(); page.on('console', msg => { const text = `${msg.text()} - ${msg.location().url}`.replace(/\s+/g, ' '); - if (!/vaadinPush/.test(text)) out("> CONSOLE:", text, '\n'); + if (!/vaadinPush|favicon.ico/.test(text)) out("> CONSOLE:", text, '\n'); }); page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); page.browser = browser; @@ -92,7 +96,7 @@ async function takeScreenshot(page, name, descr) { // Wait for the server to be ready and to get a valid response async function waitForServerReady(page, url, options = {}) { const { - maxRetries = 20, // Max number of retries + maxRetries = 35, // Max number of retries retryInterval = 5000 // Interval between retries in milliseconds } = options; @@ -103,8 +107,8 @@ async function waitForServerReady(page, url, options = {}) { const response = await page.goto(url, {timeout: 120000}); // Check if the response status is not 503 if (response && response.status() < 400) { - out(` ⏲ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); - page.waitForTimeout(1500); + green(` ✓ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); + await page.waitForTimeout(1500); return response; } else { out(` ⏲ Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 62a2197a..fc11c4ad 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -86,6 +86,7 @@ getTLs() { } checkTls() { + log "Checking TLS certificates for all ingresses hosted in the cluster" [ -n "$TEST" -o -n "$SKIPHELM" ] && return 0 for i in `kubectl get ingresses -n $CC_NS | grep nginx | awk '{print $1}'`; do getTLs "$i" @@ -151,11 +152,23 @@ runPwTests() { [ -z "$CC_CERT" -o -z "$CC_KEY" ] && NO_TLS=--notls || NO_TLS="" for f in $CC_TESTS; do runPlaywrightTests "$PIT_SCR_FOLDER/its/$f" "" "prod" "control-center" --url=https://$CC_CONTROL --login=$CC_EMAIL $NO_TLS || return 1 - [ "$f" = cc-install-apps.js ] && reloadIngress + [ "$f" = cc-install-apps.js ] && reloadIngress && checkTls || return 1 sleep 3 done } +setClusterContext() { + [ "$1" = "$CC_CLUSTER" ] && current=kind-$1 || current=$1 + ns=$2 + H=`kubectl config get-contexts | tr '*' ' ' | awk '{print $1}' | egrep "^$current$"` + [ -z "$H" ] && log "Cluster $current not found in kubectl contexts" && return 1 + runCmd "$TEST" "Setting context to $current" "kubectl config use-context $current" || return 1 + H=`kubectl config current-context` + [ "$H" != "$current" ] && log "Current context is not $current" && return 1 + runCmd "$TEST" "Setting default namespace to $ns" "kubectl config set-context --current --namespace=$ns" || return 1 + kubectl get ns >/dev/null 2>&1 || return 1 +} + ## Main method for running control center runControlCenter() { CLUSTER=${CLUSTER:-$CC_CLUSTER} @@ -167,9 +180,7 @@ runControlCenter() { [ "$CLUSTER" != "$CC_CLUSTER" ] || createKindCluster $CC_CLUSTER $CC_NS || return 1 ## Set the context to the cluster - kubectl config set-context $CLUSTER || return 1 - kubectl config set-context --current --namespace=$CC_NS || return 1 - kubectl get ns >/dev/null 2>&1 || return 1 + setClusterContext "$CLUSTER" "$CC_NS" || return 1 ## Clean up CC from a previous run unless SKIPHELM is set [ -z "$SKIPHELM" ] && uninstallCC $CLUSTER $CC_NS From 6a4ba56cfccda94786e29329ff4a57e1f5f25653 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 15:42:30 +0100 Subject: [PATCH 27/36] fix kubectl port-forward in windows --- scripts/pit/lib/lib-k8s-kind.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index ea135084..00ca834e 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -13,7 +13,7 @@ hasSUID() { ## Set SUID bit to the command # $1: command setSuid() { - isWindows && return 0 + isWindows && echo "$1" && return 0 T=/tmp/$1 for W in "$T" `which "$1"`; do hasSUID "$W" && echo "$W" && return 0 From 8cdb542b223dfdd6e070bf4ff8967940fe55adcc Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 15:54:05 +0100 Subject: [PATCH 28/36] fix return when running playwrightTests in CC --- scripts/pit/its/test-utils.js | 2 +- scripts/pit/lib/lib-ccenter.sh | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index 848f22b4..c405b735 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -107,8 +107,8 @@ async function waitForServerReady(page, url, options = {}) { const response = await page.goto(url, {timeout: 120000}); // Check if the response status is not 503 if (response && response.status() < 400) { - green(` ✓ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); await page.waitForTimeout(1500); + green(` ✓ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); return response; } else { out(` ⏲ Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index fc11c4ad..c281c324 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -152,7 +152,9 @@ runPwTests() { [ -z "$CC_CERT" -o -z "$CC_KEY" ] && NO_TLS=--notls || NO_TLS="" for f in $CC_TESTS; do runPlaywrightTests "$PIT_SCR_FOLDER/its/$f" "" "prod" "control-center" --url=https://$CC_CONTROL --login=$CC_EMAIL $NO_TLS || return 1 - [ "$f" = cc-install-apps.js ] && reloadIngress && checkTls || return 1 + if [ "$f" = cc-install-apps.js ]; then + reloadIngress && checkTls || return 1 + fi sleep 3 done } From ec851209d46bfe7811478dd6cf28d71745a7d001 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 15:56:06 +0100 Subject: [PATCH 29/36] Fix space in NPM --- scripts/pit/lib/lib-utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pit/lib/lib-utils.sh b/scripts/pit/lib/lib-utils.sh index 28fd26c5..a4f1ed59 100644 --- a/scripts/pit/lib/lib-utils.sh +++ b/scripts/pit/lib/lib-utils.sh @@ -164,7 +164,7 @@ computeGradle() { computeNpm() { _VNODE=~/.vaadin/node _NPMJS=$_VNODE/lib/node_modules/npm/bin/npm-cli.js - NPM=`which npm` + NPM="'"`which npm`"'" NPX=`which npx` NODE=`which node` [ -x "$_VNODE/bin/node" -a -f "$_NPMJS" ] && export PATH="$_VNODE/bin:$PATH" && NODE="$_VNODE/bin/node" && NPM="'$NODE' '$_NPMJS'" From 6fb8132f2a2ad948d7e38c4aaefef59ddec1d858 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 16:11:28 +0100 Subject: [PATCH 30/36] change color --- scripts/pit/its/test-utils.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index c405b735..a2f527d7 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -14,10 +14,9 @@ function log(...args) { function out(...args) { process.stderr.write(`\x1b[2m\x1b[196m${args}\x1b[0m`); } -function green(...args) { - process.stderr.write(`\x1b[2m\x1b[0;32m${args}\x1b[0m`); +function ok(...args) { + process.stderr.write(`\x1b[2m\x1b[92m${args}\x1b[0m`); } - function warn(...args) { process.stderr.write(`\x1b[2m\x1b[91m${args}\x1b[0m`); } @@ -108,7 +107,7 @@ async function waitForServerReady(page, url, options = {}) { // Check if the response status is not 503 if (response && response.status() < 400) { await page.waitForTimeout(1500); - green(` ✓ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); + ok(` ✓ Attempt ${attempt} Server is ready and returned a valid response. ${response.status()}\n`); return response; } else { out(` ⏲ Attempt ${attempt} Server is not ready yet. ${response.status()}\n`); From 29fd272c1efe7aaaac1f0b75e945c42125b5f046 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 16:26:50 +0100 Subject: [PATCH 31/36] add extra screenshot --- scripts/pit/its/cc-identity-management.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index a9e5862b..ab33544a 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -45,6 +45,7 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady await takeScreenshot(page, __filename, 'selected-app'); await page.getByRole('link', { name: 'Identity Management' }).click(); + await takeScreenshot(page, __filename, 'identity-link-clicked'); await page.getByRole('button', { name: 'Enable Identity Management' }).click(); await takeScreenshot(page, __filename, 'identity-enabled'); From b190dcc92fc41ab10cee7dede4a40e13972f52bb Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 16:48:27 +0100 Subject: [PATCH 32/36] change block place --- scripts/pit/its/cc-identity-management.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index ab33544a..2183acef 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -33,17 +33,18 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady const url = await page.locator(anchorSelectorURL).getAttribute('href'); log(`Checking that ${app} installed in ${url} is running ...\n`); - // When app is not running, localization cannot be enabled - let pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); - await waitForServerReady(pageApp, url); - await takeScreenshot(pageApp, __filename, 'app-running'); - await closePage(pageApp); log(`Enabling identity Management ...\n`); await page.locator('vaadin-select vaadin-input-container div').click(); await page.getByRole('option', { name: app }).locator('div').nth(2).click(); await takeScreenshot(page, __filename, 'selected-app'); + // When app is not running, localization button might not be enabled + let pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); + await waitForServerReady(pageApp, url); + await takeScreenshot(pageApp, __filename, 'app-running'); + await closePage(pageApp); + // Button is enabled after app is running, let's see await page.getByRole('link', { name: 'Identity Management' }).click(); await takeScreenshot(page, __filename, 'identity-link-clicked'); await page.getByRole('button', { name: 'Enable Identity Management' }).click(); From ab481d8b63962264d3c8eb725eb4d874d38dda83 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 17:40:26 +0100 Subject: [PATCH 33/36] hack to retry --- scripts/pit/its/cc-identity-management.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 2183acef..3c8180bd 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -47,7 +47,14 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady // Button is enabled after app is running, let's see await page.getByRole('link', { name: 'Identity Management' }).click(); await takeScreenshot(page, __filename, 'identity-link-clicked'); - await page.getByRole('button', { name: 'Enable Identity Management' }).click(); + try { + await page.getByRole('button', { name: 'Enable Identity Management' }).click(); + } catch (error) { + err(`Retrying in 60 secs looking for enabled button : ${error}\n`); + await page.waitForTimeout(60000); + await page.reload(); + await page.getByRole('button', { name: 'Enable Identity Management' }).click(); + } await takeScreenshot(page, __filename, 'identity-enabled'); log(`Adding Role, Group and User ...\n`); From 94dcac363eafe8770730ecbdf250f10addc44948 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 18:06:00 +0100 Subject: [PATCH 34/36] 2nd hack --- scripts/pit/its/cc-identity-management.js | 23 ++++++++++++++++++----- scripts/pit/its/test-utils.js | 4 ++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 3c8180bd..6b09a195 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -34,7 +34,6 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady log(`Checking that ${app} installed in ${url} is running ...\n`); - log(`Enabling identity Management ...\n`); await page.locator('vaadin-select vaadin-input-container div').click(); await page.getByRole('option', { name: app }).locator('div').nth(2).click(); await takeScreenshot(page, __filename, 'selected-app'); @@ -45,15 +44,29 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady await takeScreenshot(pageApp, __filename, 'app-running'); await closePage(pageApp); // Button is enabled after app is running, let's see + log(`Enabling identity Management ...\n`); await page.getByRole('link', { name: 'Identity Management' }).click(); await takeScreenshot(page, __filename, 'identity-link-clicked'); try { + await page.waitForTimeout(2000); await page.getByRole('button', { name: 'Enable Identity Management' }).click(); } catch (error) { - err(`Retrying in 60 secs looking for enabled button : ${error}\n`); - await page.waitForTimeout(60000); - await page.reload(); - await page.getByRole('button', { name: 'Enable Identity Management' }).click(); + try { + await page.getByRole('link', { name: 'Settings' }).click(); + await page.waitForTimeout(2000); + await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); + await page.getByLabel('Identity Management').check(); + await page.getByRole('button', { name: 'Update' }).click(); + await page.locator('vaadin-select vaadin-input-container div').click(); + await page.getByRole('option', { name: app }).locator('div').nth(2).click(); + await page.getByRole('link', { name: 'Identity Management' }).click(); + } catch (error) { + err(`Retrying in 60 secs looking for enabled button : ${error}\n`); + await page.waitForTimeout(60000); + await page.reload(); + await page.getByRole('link', { name: 'Identity Management' }).click(); + await page.getByRole('button', { name: 'Enable Identity Management' }).click(); + } } await takeScreenshot(page, __filename, 'identity-enabled'); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index a2f527d7..7861d048 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -21,7 +21,7 @@ function warn(...args) { process.stderr.write(`\x1b[2m\x1b[91m${args}\x1b[0m`); } function err(...args) { - process.stderr.write(`\x1b[0;31m${args}\x1b[0m`); + process.stderr.write(`\x1b[0;31m${args}\x1b[0m`.split('\n')[0] + '\n'); } const run = async (cmd) => (await promisify(exec)(cmd)).stdout; @@ -87,7 +87,7 @@ async function takeScreenshot(page, name, descr) { const scr = path.basename(name); const cnt = String(++sscount).padStart(2, "0"); const file = `${screenshots}/${scr}-${cnt}-${descr}.png`; - await page.waitForTimeout(/^win/.test(process.platform) ? 10000: 1500); + await page.waitForTimeout(/^win/.test(process.platform) ? 10000 : process.env.GITHUB_ACTIONS ? 5000 : 1500); await page.screenshot({ path: file }); out(` 📸 Screenshot taken: ${file}\n`); } From af423375f6b155371b6e78194fcb4b944e583732 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 19:20:28 +0100 Subject: [PATCH 35/36] Remove hacks --- scripts/pit/its/cc-identity-management.js | 25 +++-------------------- scripts/pit/its/cc-install-apps.js | 7 +------ scripts/pit/its/cc-localization.js | 4 +++- scripts/pit/its/test-utils.js | 3 ++- scripts/pit/lib/lib-ccenter.sh | 2 +- 5 files changed, 10 insertions(+), 31 deletions(-) diff --git a/scripts/pit/its/cc-identity-management.js b/scripts/pit/its/cc-identity-management.js index 6b09a195..6b774ae3 100644 --- a/scripts/pit/its/cc-identity-management.js +++ b/scripts/pit/its/cc-identity-management.js @@ -41,33 +41,14 @@ const {log, err, args, createPage, closePage, takeScreenshot, waitForServerReady // When app is not running, localization button might not be enabled let pageApp = await createPage(arg.headless, arg.ignoreHTTPSErrors); await waitForServerReady(pageApp, url); - await takeScreenshot(pageApp, __filename, 'app-running'); + await takeScreenshot(pageApp, __filename, `app-${app}-running`); await closePage(pageApp); // Button is enabled after app is running, let's see log(`Enabling identity Management ...\n`); await page.getByRole('link', { name: 'Identity Management' }).click(); await takeScreenshot(page, __filename, 'identity-link-clicked'); - try { - await page.waitForTimeout(2000); - await page.getByRole('button', { name: 'Enable Identity Management' }).click(); - } catch (error) { - try { - await page.getByRole('link', { name: 'Settings' }).click(); - await page.waitForTimeout(2000); - await page.locator('vaadin-grid').getByText('bakery-cc', { exact: true }).click(); - await page.getByLabel('Identity Management').check(); - await page.getByRole('button', { name: 'Update' }).click(); - await page.locator('vaadin-select vaadin-input-container div').click(); - await page.getByRole('option', { name: app }).locator('div').nth(2).click(); - await page.getByRole('link', { name: 'Identity Management' }).click(); - } catch (error) { - err(`Retrying in 60 secs looking for enabled button : ${error}\n`); - await page.waitForTimeout(60000); - await page.reload(); - await page.getByRole('link', { name: 'Identity Management' }).click(); - await page.getByRole('button', { name: 'Enable Identity Management' }).click(); - } - } + await page.waitForTimeout(2000); + await page.getByRole('button', { name: 'Enable Identity Management' }).click(); await takeScreenshot(page, __filename, 'identity-enabled'); log(`Adding Role, Group and User ...\n`); diff --git a/scripts/pit/its/cc-install-apps.js b/scripts/pit/its/cc-install-apps.js index 7a1c5b34..6357e9c1 100644 --- a/scripts/pit/its/cc-install-apps.js +++ b/scripts/pit/its/cc-install-apps.js @@ -12,8 +12,6 @@ async function installApp(app, page) { const cert = [ domain, uri ].map(a => `${a}.pem`).filter( a => fs.existsSync(a))[0] console.log(`Installing App: ${app} URI: ${uri} Cert: ${cert}`); - await takeScreenshot(page, __filename, `page-loaded-${app}`); - await page.getByRole('listitem').filter({ hasText: 'Settings'}).click() await page.getByRole('button', {name: 'Deploy'}).click() await takeScreenshot(page, __filename, `form-opened-${app}`); @@ -40,11 +38,8 @@ async function installApp(app, page) { await page.locator('.detail-layout').getByRole('button', {name: 'Deploy'}).click(); } - await takeScreenshot(page, __filename, `form-clicked-${app}`); - await page.getByRole('listitem').filter({ hasText: 'Settings'}).click() - - await takeScreenshot(page, __filename, `application-created-${app}`); + await takeScreenshot(page, __filename, `form-saved-${app}`); await expect(page.locator('vaadin-grid').getByText(app, { exact: true })).toBeVisible(); await expect(await page.getByRole('listitem').filter({ hasText: 'Applications'}) diff --git a/scripts/pit/its/cc-localization.js b/scripts/pit/its/cc-localization.js index 376c1479..c81d2c7f 100644 --- a/scripts/pit/its/cc-localization.js +++ b/scripts/pit/its/cc-localization.js @@ -80,9 +80,11 @@ const { assert } = require('console'); assert(str.includes('app.title=Panaderia')); await fs.rmSync(downloadsDir, { recursive: true }); - log(`Testing that preview page: ${previewUrl} is up and running\n`); + log(`Starting preview server\n`); await page.getByRole('button', { name: 'Start preview' }).click(); await page.waitForTimeout(5000); + + log(`Testing that preview page: ${previewUrl} is up and running\n`); const pagePrev = await createPage(arg.headless, true /* preview pages do not have a valid certificate */); await waitForServerReady(pagePrev, previewUrl); await takeScreenshot(pagePrev, __filename, 'preview-ready'); diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index 7861d048..a4ac0ea1 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -22,6 +22,7 @@ function warn(...args) { } function err(...args) { process.stderr.write(`\x1b[0;31m${args}\x1b[0m`.split('\n')[0] + '\n'); + out(args); } const run = async (cmd) => (await promisify(exec)(cmd)).stdout; @@ -70,7 +71,7 @@ async function createPage(headless, ignoreHTTPSErrors) { const page = await context.newPage(); page.on('console', msg => { const text = `${msg.text()} - ${msg.location().url}`.replace(/\s+/g, ' '); - if (!/vaadinPush|favicon.ico/.test(text)) out("> CONSOLE:", text, '\n'); + if (!/vaadinPush|favicon.ico|Autofocus/.test(text)) out("> CONSOLE:", text, '\n'); }); page.on('pageerror', e => warn("> JSERROR:", ('' + e).replace(/\s+/g, ' '), '\n')); page.browser = browser; diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index c281c324..032708bb 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -175,7 +175,7 @@ setClusterContext() { runControlCenter() { CLUSTER=${CLUSTER:-$CC_CLUSTER} - checkCommands docker kubectl helm || return 1 + checkCommands docker kubectl helm unzip || return 1 checkDockerRunning || return 1 ## Start a new kind cluster if needed From 05d3a5f9d845cf874e16f2eac3d38dace98a1095 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Thu, 30 Jan 2025 22:13:22 +0100 Subject: [PATCH 36/36] rename vars and check port 443 --- scripts/pit/its/test-utils.js | 1 + scripts/pit/lib/lib-ccenter.sh | 34 +++++++++++++++++++-------------- scripts/pit/lib/lib-k8s-kind.sh | 7 ++++++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/scripts/pit/its/test-utils.js b/scripts/pit/its/test-utils.js index a4ac0ea1..9b1fa4d1 100644 --- a/scripts/pit/its/test-utils.js +++ b/scripts/pit/its/test-utils.js @@ -65,6 +65,7 @@ async function createPage(headless, ignoreHTTPSErrors) { const browser = await chromium.launch({ headless: headless, chromiumSandbox: false, + slowMo: 500, args: ['--window-position=0,0'] }); const context = await browser.newContext({ignoreHTTPSErrors: ignoreHTTPSErrors, viewport: { width: 1792, height: 970 } }); diff --git a/scripts/pit/lib/lib-ccenter.sh b/scripts/pit/lib/lib-ccenter.sh index 032708bb..70b6a88b 100644 --- a/scripts/pit/lib/lib-ccenter.sh +++ b/scripts/pit/lib/lib-ccenter.sh @@ -14,8 +14,7 @@ CC_TLS_K=cc-control-login-tls ## Ingress names CC_ING_A=control-center CC_ING_K=control-center-keycloak-ingress -## K8s cluster and namespace -CC_CLUSTER=cc-cluster +# Namespace used for CC CC_NS=control-center ## UI tests to run after the control-center is installed @@ -73,7 +72,7 @@ uninstallCC() { [ $? = 0 ] && echo "$H" | egrep -q "^$CC_NS " || return 0 [ -n "$VERBOSE" ] && HD=--debug && KD=--v=10 runCmd "$TEST" "Uninstalling Control-Center" helm uninstall control-center --wait -n $CC_NS $HD - runCmd "$TEST" "Removing namespace $CC_NS" kubectl delete ns $CC_NS $KD + runCmd "$TEST" "Removing namespace $CC_NS" kubectl delete ns $CC_NS $KD $1 } getTLs() { @@ -86,6 +85,7 @@ getTLs() { } checkTls() { + [ -n "$TEST" ] && return 0 log "Checking TLS certificates for all ingresses hosted in the cluster" [ -n "$TEST" -o -n "$SKIPHELM" ] && return 0 for i in `kubectl get ingresses -n $CC_NS | grep nginx | awk '{print $1}'`; do @@ -97,6 +97,7 @@ reloadIngress() { [ -n "$TEST" ] && return 0 pod=`kubectl -n $CC_NS get pods | grep control-center-ingress-nginx-controller | awk '{print $1}'` || return 1 [ -n "$pod" ] && runCmd "$TEST" "Reloading nginx in $pod" "kubectl exec $pod -n "$CC_NS" -- nginx -s reload" || return 1 + [ -z "$TEST" ] && sleep 3 } ## Configure secrets for the control-center and the keycloak servers @@ -155,12 +156,11 @@ runPwTests() { if [ "$f" = cc-install-apps.js ]; then reloadIngress && checkTls || return 1 fi - sleep 3 done } setClusterContext() { - [ "$1" = "$CC_CLUSTER" ] && current=kind-$1 || current=$1 + [ "$1" = "$KIND_CLUSTER" ] && current=kind-$1 || current=$1 ns=$2 H=`kubectl config get-contexts | tr '*' ' ' | awk '{print $1}' | egrep "^$current$"` [ -z "$H" ] && log "Cluster $current not found in kubectl contexts" && return 1 @@ -173,19 +173,22 @@ setClusterContext() { ## Main method for running control center runControlCenter() { - CLUSTER=${CLUSTER:-$CC_CLUSTER} + CLUSTER=${CLUSTER:-$KIND_CLUSTER} checkCommands docker kubectl helm unzip || return 1 checkDockerRunning || return 1 ## Start a new kind cluster if needed - [ "$CLUSTER" != "$CC_CLUSTER" ] || createKindCluster $CC_CLUSTER $CC_NS || return 1 + [ "$CLUSTER" != "$KIND_CLUSTER" ] || createKindCluster $CLUSTER $CC_NS || return 1 ## Set the context to the cluster setClusterContext "$CLUSTER" "$CC_NS" || return 1 ## Clean up CC from a previous run unless SKIPHELM is set - [ -z "$SKIPHELM" ] && uninstallCC $CLUSTER $CC_NS + [ -z "$SKIPHELM" ] && uninstallCC + + ## Check if port 443 is busy + checkBusyPort "443" || return 1 ## Install Control Center installCC || return 1 @@ -199,15 +202,18 @@ runControlCenter() { installTls && checkTls || return 1 ## Forward the ingress (it needs root access since it uses port 443) - # checkBusyPort "443" - checkPort 443 || forwardIngress $CC_NS || return 1 + # checkPort "443" + [ "$CLUSTER" != "$KIND_CLUSTER" ] || forwardIngress $CC_NS || return 1 ## Run Playwright tests for the control-center runPwTests || return 1 - if [ -z "$TEST" -a -z "$KEEPCC" ]; then - stopForwardIngress || return 1 - [ "$CLUSTER" != "$CC_CLUSTER" ] || deleteKindCluster $CC_CLUSTER || return 1 - fi + stopForwardIngress || return 1 + + ## Delete the KinD cluster if it was created in this test if --keep-cc is not set + [ -n "$TEST" -o -n "$KEEPCC" -o "$CLUSTER" != "$KIND_CLUSTER" ] || deleteKindCluster "$CLUSTER" || return 1 + ## Otherwise, uninstall the control-center if --keep-cc is not set + [ -n "$TEST" -o -n "$KEEPCC" -o "$CLUSTER" = "$KIND_CLUSTER" ] || uninstallCC --wait=false || return 1 + return 0 } diff --git a/scripts/pit/lib/lib-k8s-kind.sh b/scripts/pit/lib/lib-k8s-kind.sh index 00ca834e..be4adcfd 100644 --- a/scripts/pit/lib/lib-k8s-kind.sh +++ b/scripts/pit/lib/lib-k8s-kind.sh @@ -1,5 +1,8 @@ . `dirname $0`/lib/lib-utils.sh +## Kind cluster to use if not provided +KIND_CLUSTER=cc-cluster + ## Check that the command has SUID bit set # $1: command hasSUID() { @@ -37,6 +40,7 @@ setSuid() { # $3: port in guest # $4: target port in host startPortForward() { + checkPort "$4" && err "Port $4 is already in use" && return 1 H=`getPids "kubectl port-forward $2"` [ -n "$H" ] && log "Already running k8s port-forward $1 $2 $3 -> $4 with pid $H" && return 0 [ -z "$TEST" ] && log "Starting k8s port-forward $1 $2 $3 -> $4" @@ -45,7 +49,8 @@ startPortForward() { rm -f "$bgf" runInBackgroundToFile "$K port-forward $2 $4:$3 -n $1" "$bgf" sleep 2 - egrep 'Forwarding from' "$bgf" + tail "$bgf" + egrep -q 'Forwarding from' "$bgf" } ##