Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/scripts/upload-cypress-artifacts-to-s3.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ SK="${!SK_VAR}"
BUCKET="${!BUCKET_VAR}"
REGION="${!REGION_VAR}"
PATH_PREFIX="${!PREFIX_VAR:-}"
# The store variables end with a slash (ROR_S3_PATH_E2E_REPORTS is `ror/e2e_reports/`) and the callers
# append `/build_<run id>` to them, so the key reaches us with `//` in the middle. The DGP gateway
# answers 400 to every PUT with a doubled slash, which loses all the artifacts of a red run. Squeeze
# the repeats here instead of trusting each variable to be clean.
PATH_PREFIX="$(printf '%s' "$PATH_PREFIX" | tr -s '/')"

SOURCE_DIR="${1:?Usage: upload-cypress-artifacts-to-s3.sh <results dir> <s3 subfolder>}"
S3_SUBFOLDER="${2:?Usage: upload-cypress-artifacts-to-s3.sh <results dir> <s3 subfolder>}"
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/all-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ jobs:
- name: Run E2E tests
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08
with:
max_attempts: 2
# Temporarily fail fast while validating the Cypress 15 upgrade.
max_attempts: 1
timeout_minutes: 60
retry_wait_seconds: 120
retry_on: any
command: |
./runner.sh --run e2e --env ${{ matrix.env }} --elk ${{ matrix.version }}
env:
ROR_ACTIVATION_KEY: ${{ secrets.ROR_ENT_ACTIVATION_TOKEN }}
ELECTRON_EXTRA_LAUNCH_ARGS: '--disable-gpu'
- name: Stop Docker memory monitor
if: always()
uses: ./.github/docker-memory-monitor
Expand Down Expand Up @@ -163,7 +163,8 @@ jobs:
- name: Run E2E tests
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08
with:
max_attempts: 2
# Temporarily fail fast while validating the Cypress 15 upgrade.
max_attempts: 1
timeout_minutes: 60
retry_wait_seconds: 120
retry_on: any
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/targeted-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ jobs:
./runner.sh "${PARAMS[@]}"
env:
ROR_ACTIVATION_KEY: ${{ secrets.ROR_ENT_ACTIVATION_TOKEN }}
ELECTRON_EXTRA_LAUNCH_ARGS: '--disable-gpu'
ENV: ${{ github.event.inputs.env }}
ELK_VERSION: ${{ github.event.inputs.elk_version }}
ROR_ES_VERSION: ${{ github.event.inputs.ror_es_version }}
Expand Down
7 changes: 3 additions & 4 deletions e2e-tests/cypress/e2e/Sanity-check.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,9 @@ describe('sanity check', () => {
Reporting.downloadAndVerifyReportExists('admin_search');

cy.log('Change tenancy, and initialize it');
const finishUrl =
semver.gte(getKibanaVersion(), '8.19.0') && semver.lt(getKibanaVersion(), '9.0.0')
? '/app/management/insightsAndAlerting/reporting/exports'
: '/app/management/insightsAndAlerting/reporting';
const finishUrl = semver.satisfies(getKibanaVersion(), '>=8.19.0 <9.0.0 || >=9.1.0')
? '/app/management/insightsAndAlerting/reporting/exports'
: '/app/management/insightsAndAlerting/reporting';

RorMenu.changeTenancy('Infosec', finishUrl);

Expand Down
3 changes: 3 additions & 0 deletions e2e-tests/cypress/e2e/Tenancy.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ describe('Tenancy', () => {
) => {
RorMenu.changeTenancy('administrators', endUrl, '');
cy.go('back');
cy.urlShouldMatch(endUrl);
cy.reload();
cy.get('[data-test-subj=globalLoadingIndicator-hidden]').should('be.visible');
};

// eslint-disable-next-line no-use-before-define
Expand Down
53 changes: 52 additions & 1 deletion e2e-tests/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,58 @@ Cypress.Commands.add('urlShouldMatch', (urlPattern: string) => {
return cy.url().should('match', new RegExp(`${baseUrl}${escapedPath}${suffix}$`));
});

Cypress.Commands.add('getValueFromClipboard', () => cy.window().then(win => win.navigator.clipboard.readText()));
/**
* Headless Linux has no system clipboard for Electron. Keep clipboard behavior in the AUT so the
* tests validate exactly what Kibana copies without relying on the runner's desktop session.
*/
let copiedText = '';

Cypress.on('test:before:run', () => {
copiedText = '';
});

Cypress.on('window:before:load', win => {
Object.defineProperty(win.navigator, 'clipboard', {
configurable: true,
value: {
readText: () => Promise.resolve(copiedText),
writeText: (text: string) => {
copiedText = text;
return Promise.resolve();
}
}
});

const execCommand = win.document.execCommand.bind(win.document);
Object.defineProperty(win.document, 'execCommand', {
configurable: true,
value: (commandId: string, showUI?: boolean, value?: string) => {
if (commandId !== 'copy') {
return execCommand(commandId, showUI, value);
}

const active = win.document.activeElement;
const isField = active instanceof win.HTMLInputElement || active instanceof win.HTMLTextAreaElement;
const selectedText = (isField ? active.value : win.getSelection()?.toString()) ?? '';
const clipboardData = new win.DataTransfer();
const copyEvent = new win.ClipboardEvent('copy', { bubbles: true, cancelable: true, clipboardData });
(active ?? win.document).dispatchEvent(copyEvent);
copiedText = clipboardData.getData('text/plain') || selectedText;
return true;
}
});
});

Cypress.Commands.add('getValueFromClipboard', () =>
cy
.wrap(null, { log: false })
.should(() => expect(copiedText, 'clipboard text').not.to.be.empty)
.then(() => {
const text = copiedText;
copiedText = '';
return text;
})
);

Cypress.on('uncaught:exception', (err, runnable) => {
/**
Expand Down
21 changes: 16 additions & 5 deletions e2e-tests/cypress/support/page-objects/Discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,23 @@ export class Discover {
cy.contains('Discover').click();
cy.get('[data-test-subj=discoverSaveButton]').click();
cy.get('[data-test-subj=savedObjectTitle]').type(reportName, { delay: 0 });
cy.get('[data-test-subj=confirmSaveSavedObjectButton]').click({ force: true });
const usesContentManagement = semver.gte(getKibanaVersion(), '8.19.0');
const saveSearchUrl = usesContentManagement
? '**/api/content_management/rpc/create'
: '**/api/saved_objects/search*';
cy.intercept('POST', saveSearchUrl).as('saveSearch');
cy.get('[data-test-subj=confirmSaveSavedObjectButton]').should('be.enabled').click({ force: true });
cy.get('[data-test-subj=savedObjectTitle]').should('not.exist');
cy.contains('was saved', { timeout: 10000 }).should('exist');
cy.wait('@saveSearch').then(({ response }) => {
expect(response?.statusCode).to.equal(200);
const savedSearchId = usesContentManagement ? response?.body.result.result.item.id : response?.body.id;
expect(savedSearchId).to.be.a('string');

cy.findByRole('navigation', {
name: /breadcrumb/i
}).findByText(reportName);
if (!usesContentManagement) {
cy.url().should('include', `/view/${savedSearchId}`);
}
});
}

static exportToCsv() {
Expand Down Expand Up @@ -176,7 +187,7 @@ export class Discover {
cy.getByDataTestSubj('superDatePickerToggleQuickMenuButton').click();
cy.getByDataTestSubj('superDatePickerCommonlyUsed_Today').click();
}

cy.wait('@search');
};

Expand Down
22 changes: 15 additions & 7 deletions e2e-tests/cypress/support/page-objects/Loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,31 @@ import { TENANCY_QUERY_STRING_KEY } from '../types';
export class Loader {
public static loading(finishUrl?: string, spacePrefix?: string) {
cy.log('loading');
this.start();
this.finish(finishUrl, spacePrefix);
}

public static waitForBreadcrumb(breadcrumb: string) {
cy.getByDataTestSubj('breadcrumb first last').contains(breadcrumb);
}

private static start() {
cy.log('loading start');
cy.contains('Loading Elastic', { timeout: 80000 }).should('exist');
}

private static finish(finishUrl = `/app/home?${TENANCY_QUERY_STRING_KEY}=*`, spacePrefix = '/s/default') {
cy.log('loading finish');
cy.contains('Loading Elastic', { timeout: 80000 }).should('not.exist');
cy.location().then(({ pathname, search }) => {
if (pathname !== '/spaces/space_selector') {
return;
}

const tenancy = new URLSearchParams(search).get(TENANCY_QUERY_STRING_KEY);
const target = `${spacePrefix}${finishUrl}`.replace(
`${TENANCY_QUERY_STRING_KEY}=*`,
`${TENANCY_QUERY_STRING_KEY}=${encodeURIComponent(tenancy ?? '')}`
);

// Replace the selector interstitial so browser back still reaches the page before tenancy changed.
cy.window().then(win => win.location.replace(target));
});
cy.urlShouldMatch(`${spacePrefix}${finishUrl}`);
cy.get('[data-test-subj=globalLoadingIndicator-hidden]').should('be.visible');
cy.get('[data-test-subj=globalLoadingIndicator-hidden]').should('exist');
}
}
2 changes: 1 addition & 1 deletion e2e-tests/cypress/support/page-objects/Reporting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class Reporting {

static verifyIfReportingPageAfterRefresh() {
cy.log('Verify if reporting page open after refresh');
const expectedUrl = semver.satisfies(getKibanaVersion(), '>=8.19.0 <9.0.0')
const expectedUrl = semver.satisfies(getKibanaVersion(), '>=8.19.0 <9.0.0 || >=9.1.0')
? `${Cypress.config().baseUrl}/s/default/app/management/insightsAndAlerting/reporting/exports`
: `${Cypress.config().baseUrl}/s/default/app/management/insightsAndAlerting/reporting`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class RoAndRoStrictKibanaAccessAssertions {
kbnApiClient.loadSampleData('ecommerce', credentials, 'template_group');
Settings.setSettingsData(fixtureYamlFileName);
Login.initialization();
RoAndRoStrictKibanaAccessAssertions.changeTenancyAndAwaitSpaces('template');
RorMenu.changeTenancy('template');
Home.loadSampleDataButtonHidden();

cy.log('Verify Dashboard features');
Expand Down Expand Up @@ -111,14 +111,4 @@ export class RoAndRoStrictKibanaAccessAssertions {
IndexPattern.addIndexButtonHidden();
IndexPattern.rowEditItemButtonsHidden();
}

private static changeTenancyAndAwaitSpaces(tenancyName: string) {
if (semver.gte(getKibanaVersion(), '9.4.0')) {
cy.intercept('*/bundles/plugin/spaces/1.0.0/spaces.chunk*').as('spacesPlugin');
}
RorMenu.changeTenancy(tenancyName);
if (semver.gte(getKibanaVersion(), '9.4.0')) {
cy.wait('@spacesPlugin');
}
}
}
28 changes: 22 additions & 6 deletions e2e-tests/cypress/support/page-objects/RorMenu.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Loader } from './Loader';
import semver from 'semver';
import { getKibanaVersion } from '../helpers';

export class RorMenu {
private static readonly TRIGGER = '#rorMenuPopover';
private static readonly PANEL = '#rorMenuPanel';

static openRorMenu() {
cy.get('#rorMenuPopover').click();
RorMenu.clickTriggerUntilOpen();
cy.get(RorMenu.PANEL, { timeout: 10000 }).should('exist');
}

static closeRorMenu() {
cy.get('#rorMenuPopover').click();
cy.get(RorMenu.TRIGGER).click();
}

static openEditSecuritySettings() {
cy.intercept('GET', '/pkp/api/settings').as('getSettings');
cy.contains('Edit security settings').click({ force: true });
cy.get(RorMenu.PANEL).contains('Edit security settings').click({ force: true });
cy.wait('@getSettings').then(({ response }) => {
expect([200, 304]).to.include(response.statusCode);
});
Expand All @@ -37,11 +39,25 @@ export class RorMenu {

static openDataViewsPage() {
cy.log('open data views page');
cy.get('#rorMenuPopover').click();
RorMenu.openRorMenu();
cy.get('.ror_kibana_management').click({ force: true });
cy.get('.euiButtonEmpty').contains('Data View', { matchCase: false }).click({ force: true });
}

private static clickTriggerUntilOpen(attempt = 1): Cypress.Chainable {
return cy
.get(RorMenu.TRIGGER, { timeout: 30000 })
.click()
.wait(300, { log: false })
.get('body', { log: false })
.then($body => {
if ($body.find(RorMenu.PANEL).length === 0 && attempt < 3) {
return RorMenu.clickTriggerUntilOpen(attempt + 1);
}
return $body;
});
}

static pressLogoutButton() {
cy.contains('Log out').click();
}
Expand Down
3 changes: 1 addition & 2 deletions e2e-tests/cypress/support/page-objects/Spaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ export class Spaces {

static createNewSpace(spaceName: string) {
cy.log('Create new space');
cy.get('[data-test-subj=spacesNavSelector]').click();
cy.get('[data-test-subj=manageSpaces]').click({ force: true });
cy.location('search').then(search => cy.visit(`/s/default/app/management/kibana/spaces${search}`));
cy.get('[data-test-subj=createSpace]').click();
cy.get('[data-test-subj=addSpaceName]').type(spaceName);
cy.get('#featureCategoryCheckbox_kibana').uncheck();
Expand Down
4 changes: 2 additions & 2 deletions e2e-tests/cypress/support/page-objects/Tenancy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export class Tenancy {
'U2FsdGVkX19NA4zo3j%2BFfqlrhGwAoRnqfKlt4mELK8JYITdrBgNrxNAkXTvRn%2FAUmarKlnZBqKBK0592NX%2FWyer%2B2CvTOaL1T1PH0FoUWvEJEu7L1crZPcYG1WMike82';

static checkTenancyNameInBadge(tenancyName: string, kibanaAccess: 'a' | 'rw' | 'ro' | 'ro_strict') {
cy.get('[data-testid="tenant-indicator"]').as('tenancyIndicator').trigger('mouseover');
cy.get('@tenancyIndicator').should('have.text', `${tenancyName}${kibanaAccess}`);
cy.get('[data-testid="tenant-indicator"]').trigger('mouseover');
cy.get('[data-testid="tenant-indicator"]').should('have.text', `${tenancyName}${kibanaAccess}`);
}

static getTenancyFromUrl() {
Expand Down
6 changes: 3 additions & 3 deletions e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"license": "Beshu Limited, All rights reserved",
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
"dependencies": {
"@testing-library/cypress": "^10.0.3",
"cypress": "14.5.4",
"cypress-network-idle": "^1.15.0",
"@testing-library/cypress": "^10.1.3",
"cypress": "15.21.0",
"cypress-network-idle": "^2.0.1",
"form-data": "^4.0.4",
"js-yaml": "^4.1.1",
"node-fetch": "2.6.7",
Expand Down
22 changes: 15 additions & 7 deletions e2e-tests/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ if [ $# -lt 1 ]; then
exit 1
fi

export ELECTRON_EXTRA_LAUNCH_ARGS="--disable-gpu"
KBN_VERSION="$1"
ENV_NAME="$2"
RUN_TYPE="${3:-run}" # Default to "run" if not provided
Expand All @@ -50,12 +49,21 @@ yarn --frozen-lockfile install
if [[ "$RUN_TYPE" == "open" ]]; then
yarn open --env="kibanaVersion=$KBN_VERSION,enterpriseActivationKey=$ROR_ACTIVATION_KEY,envName=$ENV_NAME"
else
yarn run run --env="kibanaVersion=$KBN_VERSION,enterpriseActivationKey=$ROR_ACTIVATION_KEY,envName=$ENV_NAME"
fi
mkdir -p ../results

if [[ $? -ne 0 ]]; then
echo "❌ E2E tests failed :("
exit 1
set +e
yarn run run --env="kibanaVersion=$KBN_VERSION,enterpriseActivationKey=$ROR_ACTIVATION_KEY,envName=$ENV_NAME" 2>&1 |
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
printf '%s\n' "${LINE//"$ROR_ACTIVATION_KEY"/***}"
done |
tee ../results/cypress.log
CYPRESS_EXIT_CODE=${PIPESTATUS[0]}
set -e

if [[ "$CYPRESS_EXIT_CODE" -ne 0 ]]; then
echo "❌ E2E tests failed :("
exit "$CYPRESS_EXIT_CODE"
fi
fi

echo "✅ E2E tests result: SUCCESS"
echo "✅ E2E tests result: SUCCESS"
Loading
Loading