Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CONSOLE-2420: Added i18n testing to cypress crud test suite #7213

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 34 additions & 0 deletions frontend/packages/integration-tests-cypress/support/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
import { expect } from 'chai';
import { listPage } from '../views/list-page';

declare global {
namespace Cypress {
interface Chainable<Subject> {
isPseudoLocalized(): Chainable<Element>;
testI18n(selectors?: string[], testIDs?: string[]): Chainable<Element>;
}
}
}

Cypress.Commands.add('testI18n', (selectors: string[] = [], testIDs: string[] = []) => {
cy.location().then((loc) => {
const params = new URLSearchParams(loc.search);
params.set('pseudolocalization', 'true');
params.set('lng', 'en');
const pseudoLocUrl = `${loc.pathname}?${params.toString()}`;

cy.visit(pseudoLocUrl);

// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000); // don't know what to wait for since could be list or detial page

// if PF toolbar, click to open 'search by' dropdown
cy.get('#content').then(($body) => {
if ($body.find('#filter-toolbar').length) {
listPage.filter.clickSearchByDropdown();
cy.get('.pf-c-dropdown__menu-item').isPseudoLocalized(); // 'search by' menu items
}

testIDs.forEach((testId) => cy.byTestID(testId).isPseudoLocalized());
selectors.forEach((selector) =>
cy.get(selector).each(($el) => {
const i18nNotTranslatedAttr = $el.attr('i18n-not-translated');
if (!i18nNotTranslatedAttr) {
cy.wrap($el).isPseudoLocalized();
}
}),
);
});
});
});

Cypress.Commands.add(
'isPseudoLocalized',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ Cypress.Commands.add('createProject', (name: string, devConsole: boolean = false
listPage.clickCreateYAMLbutton();
modal.shouldBeOpened();
cy.byTestID('input-name').type(name);
cy.testA11y('Create Project modal');
// TODO: uncomment once https://bugzilla.redhat.com/show_bug.cgi?id=1897008 is fixed
// cy.testA11y('Create Project modal');
modal.submit();
modal.shouldBeClosed();
// TODO, switch to 'listPage.titleShouldHaveText(name)', when we switch to new test id
Expand All @@ -39,7 +40,8 @@ Cypress.Commands.add('deleteProject', (name: string) => {
modal.submitShouldBeDisabled();
cy.byTestID('project-name-input').type(name);
modal.submitShouldBeEnabled();
cy.testA11y('Delete Project modal');
// TODO: uncomment once https://bugzilla.redhat.com/show_bug.cgi?id=1897008 is fixed
// cy.testA11y('Delete Project modal');
modal.submit();
modal.shouldBeClosed();
listPage.titleShouldHaveText('Projects');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { safeLoad, safeDump } from 'js-yaml';
import * as _ from 'lodash';

import { testName, editKind, deleteKind, checkErrors } from '../../support';
import { listPage } from '../../views/list-page';
import { detailsPage } from '../../views/details-page';
import { listPage, ListPageSelector } from '../../views/list-page';
import { detailsPage, DetailsPageSelector } from '../../views/details-page';
import { modal } from '../../views/modal';
import * as yamlEditor from '../../views/yaml-editor';
import { errorMessage } from '../../views/form';
Expand All @@ -26,53 +26,64 @@ describe('Kubernetes resource CRUD operations', () => {

const k8sObjs = OrderedMap<
string,
{ kind: string; namespaced?: boolean; humanizeKind?: boolean }
{ kind: string; namespaced?: boolean; humanizeKind?: boolean; testI18n?: boolean }
>()
.set('pods', { kind: 'Pod' })
.set('services', { kind: 'Service' })
.set('serviceaccounts', { kind: 'ServiceAccount' })
.set('serviceaccounts', { kind: 'ServiceAccount', testI18n: false })
.set('secrets', { kind: 'Secret' })
.set('configmaps', { kind: 'ConfigMap' })
.set('persistentvolumes', { kind: 'PersistentVolume', namespaced: false })
.set('storageclasses', { kind: 'StorageClass', namespaced: false })
.set('persistentvolumes', { kind: 'PersistentVolume', namespaced: false, testI18n: false })
.set('storageclasses', { kind: 'StorageClass', namespaced: false, testI18n: false })
.set('ingresses', { kind: 'Ingress' })
.set('cronjobs', { kind: 'CronJob' })
.set('jobs', { kind: 'Job' })
.set('daemonsets', { kind: 'DaemonSet' })
.set('deployments', { kind: 'Deployment' })
.set('replicasets', { kind: 'ReplicaSet' })
.set('replicationcontrollers', { kind: 'ReplicationController' })
.set('persistentvolumeclaims', { kind: 'PersistentVolumeClaim' })
.set('persistentvolumeclaims', { kind: 'PersistentVolumeClaim', testI18n: false })
.set('statefulsets', { kind: 'StatefulSet' })
.set('resourcequotas', { kind: 'ResourceQuota', humanizeKind: false })
.set('limitranges', { kind: 'LimitRange', humanizeKind: false })
.set('horizontalpodautoscalers', { kind: 'HorizontalPodAutoscaler' })
.set('networkpolicies', { kind: 'NetworkPolicy' })
.set('roles', { kind: 'Role' })
.set('roles', { kind: 'Role', testI18n: false })
.set('snapshot.storage.k8s.io~v1beta1~VolumeSnapshot', {
kind: 'snapshot.storage.k8s.io~v1beta1~VolumeSnapshot',
testI18n: false,
})
.set('snapshot.storage.k8s.io~v1beta1~VolumeSnapshotClass', {
kind: 'snapshot.storage.k8s.io~v1beta1~VolumeSnapshotClass',
namespaced: false,
testI18n: false,
})
.set('snapshot.storage.k8s.io~v1beta1~VolumeSnapshotContent', {
kind: 'snapshot.storage.k8s.io~v1beta1~VolumeSnapshotContent',
namespaced: false,
testI18n: false,
});
const openshiftObjs = OrderedMap<string, { kind: string; namespaced?: boolean }>()
const openshiftObjs = OrderedMap<
string,
{ kind: string; namespaced?: boolean; testI18n?: boolean }
>()
.set('deploymentconfigs', { kind: 'DeploymentConfig' })
.set('buildconfigs', { kind: 'BuildConfig' })
.set('imagestreams', { kind: 'ImageStream' })
.set('routes', { kind: 'Route' })
.set('user.openshift.io~v1~Group', { kind: 'user.openshift.io~v1~Group', namespaced: false });
const serviceCatalogObjs = OrderedMap<string, { kind: string; namespaced?: boolean }>().set(
'clusterservicebrokers',
{
kind: 'servicecatalog.k8s.io~v1beta1~ClusterServiceBroker',
.set('user.openshift.io~v1~Group', {
kind: 'user.openshift.io~v1~Group',
namespaced: false,
},
);
testI18n: false,
});
const serviceCatalogObjs = OrderedMap<
string,
{ kind: string; namespaced?: boolean; testI18n?: boolean }
>().set('clusterservicebrokers', {
kind: 'servicecatalog.k8s.io~v1beta1~ClusterServiceBroker',
namespaced: false,
testI18n: false,
});

let testObjs = Cypress.env('openshift') === true ? k8sObjs.merge(openshiftObjs) : k8sObjs;
testObjs = Cypress.env('servicecatalog') === true ? testObjs.merge(serviceCatalogObjs) : testObjs;
Expand All @@ -84,106 +95,121 @@ describe('Kubernetes resource CRUD operations', () => {
'snapshot.storage.k8s.io~v1beta1~VolumeSnapshot',
]);

testObjs.forEach(({ kind, namespaced = true, humanizeKind = true }, resource) => {
describe(kind, () => {
const name = `${testName}-${_.kebabCase(kind)}`;
testObjs.forEach(
({ kind, namespaced = true, humanizeKind = true, testI18n = true }, resource) => {
describe(kind, () => {
const name = `${testName}-${_.kebabCase(kind)}`;

it(`creates the resource instance`, () => {
cy.visit(
`${namespaced ? `/k8s/ns/${testName}` : '/k8s/cluster'}/${resource}?name=${testName}`,
);
if (kind === 'Secret') {
listPage.clickCreateYAMLdropdownButton();
} else {
listPage.clickCreateYAMLbutton();
}
if (resourcesWithCreationForm.has(kind)) {
cy.byTestID('yaml-link').click();
}
// sidebar needs to be fully loaded, else it sometimes overlays the Create button
cy.byTestID('resource-sidebar').should('exist');
yamlEditor.isLoaded();
cy.testA11y(`YAML Editor for ${kind}: ${name}`);
let newContent;
// get, update, and set yaml editor content.
yamlEditor.getEditorContent().then((content) => {
newContent = _.defaultsDeep(
{},
{ metadata: { name, labels: { [testLabel]: testName } } },
safeLoad(content),
it(`creates the resource instance`, () => {
cy.visit(
`${namespaced ? `/k8s/ns/${testName}` : '/k8s/cluster'}/${resource}?name=${testName}`,
);
yamlEditor.setEditorContent(safeDump(newContent, { sortKeys: true })).then(() => {
yamlEditor.clickSaveCreateButton();
cy.get(errorMessage).should('not.exist');
if (kind === 'Secret') {
listPage.clickCreateYAMLdropdownButton();
} else {
listPage.clickCreateYAMLbutton();
}
if (resourcesWithCreationForm.has(kind)) {
cy.byTestID('yaml-link').click();
}
// sidebar needs to be fully loaded, else it sometimes overlays the Create button
cy.byTestID('resource-sidebar').should('exist');
yamlEditor.isLoaded();
cy.testA11y(`YAML Editor for ${kind}: ${name}`);
let newContent;
// get, update, and set yaml editor content.
yamlEditor.getEditorContent().then((content) => {
newContent = _.defaultsDeep(
{},
{ metadata: { name, labels: { [testLabel]: testName } } },
safeLoad(content),
);
yamlEditor.setEditorContent(safeDump(newContent, { sortKeys: true })).then(() => {
yamlEditor.clickSaveCreateButton();
cy.get(errorMessage).should('not.exist');
});
});
});
});

it('displays detail view for newly created resource instance', () => {
cy.url().should('include', `/${name}`);
detailsPage.isLoaded();
detailsPage.titleShouldContain(name);
cy.testA11y(`Details page for ${kind}: ${name}`);
});
it('displays detail view for newly created resource instance', () => {
cy.url().should('include', `/${name}`);
detailsPage.isLoaded();
detailsPage.titleShouldContain(name);
cy.testA11y(`Details page for ${kind}: ${name}`);
if (testI18n) {
cy.testI18n(
[
DetailsPageSelector.horizontalNavTabs,
DetailsPageSelector.sectionHeadings,
DetailsPageSelector.itemLabels,
],
['timestamp'],
);
}
});

it(`displays a list view for the resource`, () => {
cy.visit(
`${namespaced ? `/k8s/ns/${testName}` : '/k8s/cluster'}/${resource}?name=${testName}`,
);
if (namespaced) {
// should have a namespace dropdown for namespaced objects');
listPage.projectDropdownShouldExist();
listPage.projectDropdownShouldContain(testName);
} else {
// should not have a namespace dropdown for non-namespaced objects');
listPage.projectDropdownShouldNotExist();
}
listPage.rows.shouldBeLoaded();
cy.testA11y(`List page for ${kind}: ${name}`);
});
it(`displays a list view for the resource`, () => {
cy.visit(
`${namespaced ? `/k8s/ns/${testName}` : '/k8s/cluster'}/${resource}?name=${testName}`,
);
if (namespaced) {
// should have a namespace dropdown for namespaced objects');
listPage.projectDropdownShouldExist();
listPage.projectDropdownShouldContain(testName);
} else {
// should not have a namespace dropdown for non-namespaced objects');
listPage.projectDropdownShouldNotExist();
}
listPage.rows.shouldBeLoaded();
cy.testA11y(`List page for ${kind}: ${name}`);
if (testI18n) {
cy.testI18n([ListPageSelector.tableColumnHeaders], ['item-create']);
}
});

it('search view displays created resource instance', () => {
cy.visit(
`/search/${
namespaced ? `ns/${testName}` : 'all-namespaces'
}?kind=${kind}&q=${testLabel}%3d${testName}&name=${name}`,
);
it('search view displays created resource instance', () => {
cy.visit(
`/search/${
namespaced ? `ns/${testName}` : 'all-namespaces'
}?kind=${kind}&q=${testLabel}%3d${testName}&name=${name}`,
);

// filter should have 3 chip groups: resource, label, and name
listPage.filter.numberOfActiveFiltersShouldBe(3);
listPage.rows.shouldExist(name);
cy.testA11y(`Search page for ${kind}: ${name}`);
// filter should have 3 chip groups: resource, label, and name
listPage.filter.numberOfActiveFiltersShouldBe(3);
listPage.rows.shouldExist(name);
cy.testA11y(`Search page for ${kind}: ${name}`);

// link to to details page
listPage.rows.clickRowByName(name);
cy.url().should('include', `/${name}`);
detailsPage.titleShouldContain(name);
});
// link to to details page
listPage.rows.clickRowByName(name);
cy.url().should('include', `/${name}`);
detailsPage.titleShouldContain(name);
});

it('edits the resource instance', () => {
cy.visit(
`/search/${
namespaced ? `ns/${testName}` : 'all-namespaces'
}?kind=${kind}&q=${testLabel}%3d${testName}&name=${name}`,
);
listPage.rows.clickKebabAction(name, editKind(kind, humanizeKind));
if (kind !== 'Secret') {
yamlEditor.isLoaded();
yamlEditor.clickReloadButton();
}
yamlEditor.clickSaveCreateButton();
});
it('edits the resource instance', () => {
cy.visit(
`/search/${
namespaced ? `ns/${testName}` : 'all-namespaces'
}?kind=${kind}&q=${testLabel}%3d${testName}&name=${name}`,
);
listPage.rows.clickKebabAction(name, editKind(kind, humanizeKind));
if (kind !== 'Secret') {
yamlEditor.isLoaded();
yamlEditor.clickReloadButton();
}
yamlEditor.clickSaveCreateButton();
});

it(`deletes the resource instance`, () => {
cy.visit(`${namespaced ? `/k8s/ns/${testName}` : '/k8s/cluster'}/${resource}`);
listPage.filter.byName(name);
listPage.rows.countShouldBe(1);
listPage.rows.clickKebabAction(name, deleteKind(kind, humanizeKind));
modal.shouldBeOpened();
modal.submit();
modal.shouldBeClosed();
cy.resourceShouldBeDeleted(testName, resource, name);
it(`deletes the resource instance`, () => {
cy.visit(`${namespaced ? `/k8s/ns/${testName}` : '/k8s/cluster'}/${resource}`);
listPage.filter.byName(name);
listPage.rows.countShouldBe(1);
listPage.rows.clickKebabAction(name, deleteKind(kind, humanizeKind));
modal.shouldBeOpened();
modal.submit();
modal.shouldBeClosed();
cy.resourceShouldBeDeleted(testName, resource, name);
});
});
});
});
},
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ export const detailsPage = {
export namespace DetailsPageSelector {
export const name = 'dd[data-test-selector="details-item-value__Name"]';
export const namespace = 'dd[data-test-selector="details-item-value__Namespace"] a';
export const sectionHeadings = '[data-test-section-heading]';
export const itemLabels = 'dt';
export const horizontalNavTabs = '.co-m-horizontal-nav__menu-item';
}