mirror of https://github.com/rancher/dashboard.git
Create e2e test for `configmap`
This test allows us to assert that blocks containing long lines (over 80 characters) entered into a configmap value field will output the correct value upon saving. The intent of this test is to assert that the regression described in #10044 is resolved and will not be reintroduced if further changes are made to `create-yaml`. Signed-off-by: Phillip Rak <rak.phillip@gmail.com>
This commit is contained in:
parent
01c043099e
commit
a6b3c1c927
|
|
@ -0,0 +1,52 @@
|
|||
import ComponentPo from '@/cypress/e2e/po/components/component.po';
|
||||
import { CypressChainable } from '@/cypress/e2e/po/po.types';
|
||||
|
||||
export default class CodeMirrorPo extends ComponentPo {
|
||||
static byLabel(self: CypressChainable, label: string): CodeMirrorPo {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
static bySelector(self: CypressChainable, selector: string): CodeMirrorPo {
|
||||
return new CodeMirrorPo(
|
||||
self
|
||||
.find(`${ selector } .CodeMirror`, { includeShadowDom: true })
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type value in the input
|
||||
* @param value Value to be typed
|
||||
* @returns
|
||||
*/
|
||||
set(value: string): Cypress.Chainable {
|
||||
this.input().should('be.visible');
|
||||
|
||||
return this.input()
|
||||
.then(($codeMirror) => {
|
||||
const codeMirrorInstance = $codeMirror[0].CodeMirror;
|
||||
|
||||
codeMirrorInstance.setValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
return this.input().clear();
|
||||
}
|
||||
|
||||
value(): Cypress.Chainable {
|
||||
return this.input()
|
||||
.then(($codeMirror) => {
|
||||
const codeMirrorInstance = $codeMirror[0].CodeMirror;
|
||||
|
||||
return codeMirrorInstance.getValue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the input HTML element from given container
|
||||
* @returns HTML Element
|
||||
*/
|
||||
private input(): Cypress.Chainable {
|
||||
return this.self();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import ComponentPo from '@/cypress/e2e/po/components/component.po';
|
||||
import { CypressChainable } from '@/cypress/e2e/po/po.types';
|
||||
|
||||
export default class InputPo extends ComponentPo {
|
||||
static byLabel(self: CypressChainable, label: string): InputPo {
|
||||
return new InputPo(
|
||||
self
|
||||
.find('.labeled-input', { includeShadowDom: true })
|
||||
.contains(label)
|
||||
.next()
|
||||
);
|
||||
}
|
||||
|
||||
static bySelector(self: CypressChainable, selector: string): InputPo {
|
||||
return new InputPo(
|
||||
self
|
||||
.find(`input${ selector }`, { includeShadowDom: true }).eq(0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type value in the input
|
||||
* @param value Value to be typed
|
||||
* @returns
|
||||
*/
|
||||
set(value: string): Cypress.Chainable {
|
||||
this.input().should('be.visible');
|
||||
this.input().focus();
|
||||
this.input().clear();
|
||||
|
||||
return this.input().type(value);
|
||||
}
|
||||
|
||||
clear() {
|
||||
return this.input().clear();
|
||||
}
|
||||
|
||||
value(): Cypress.Chainable {
|
||||
throw new Error('Not implements');
|
||||
// The text for the input field is in a shadow dom element. Neither the proposed two methods
|
||||
// to dive in to the shadow dom work
|
||||
// return this.input().find('div', { includeShadowDom: true }).invoke('text');
|
||||
// return this.input().shadow().find('div').invoke('text');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the input HTML element from given container
|
||||
* @returns HTML Element
|
||||
*/
|
||||
private input(): Cypress.Chainable {
|
||||
return this.self();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import CreateEditViewPo from '@/cypress/e2e/po/components/create-edit-view.po';
|
||||
import AsyncButtonPo from '@/cypress/e2e/po/components/async-button.po';
|
||||
import LabeledInputPo from '@/cypress/e2e/po/components/labeled-input.po';
|
||||
import InputPo from '@/cypress/e2e/po/components/input.po';
|
||||
import CodeMirrorPo from '@/cypress/e2e/po/components/code-mirror.po';
|
||||
|
||||
export default class ConfigMapPo extends CreateEditViewPo {
|
||||
constructor(selector = '.dashboard-root') {
|
||||
super(selector);
|
||||
}
|
||||
|
||||
nameInput() {
|
||||
return LabeledInputPo.bySelector(this.self(), '[data-testid="name-ns-description-name"]');
|
||||
}
|
||||
|
||||
keyInput() {
|
||||
return InputPo.bySelector(this.self(), '[data-testid="input-kv-item-key-0"]');
|
||||
}
|
||||
|
||||
valueInput() {
|
||||
return CodeMirrorPo.bySelector(this.self(), '[data-testid="code-mirror-multiline-field"]');
|
||||
}
|
||||
|
||||
descriptionInput() {
|
||||
return LabeledInputPo.byLabel(this.self(), 'Description');
|
||||
}
|
||||
|
||||
saveCreateForm(): AsyncButtonPo {
|
||||
return new AsyncButtonPo('[data-testid="form-save"]', this.self());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import PagePo from '@/cypress/e2e/po/pages/page.po';
|
||||
import BaseResourceList from '@/cypress/e2e/po/lists/base-resource-list.po';
|
||||
|
||||
export class ConfigMapPagePo extends PagePo {
|
||||
private static createPath(clusterId: string) {
|
||||
return `/c/${ clusterId }/explorer/configmap`;
|
||||
}
|
||||
|
||||
static goTo(clusterId: string): Cypress.Chainable<Cypress.AUTWindow> {
|
||||
return super.goTo(ConfigMapPagePo.createPath(clusterId));
|
||||
}
|
||||
|
||||
constructor(clusterId = 'local') {
|
||||
super(ConfigMapPagePo.createPath(clusterId));
|
||||
}
|
||||
|
||||
clickCreate() {
|
||||
const baseResourceList = new BaseResourceList(this.self());
|
||||
|
||||
return baseResourceList.masthead().actions().eq(0).click();
|
||||
}
|
||||
|
||||
listElementWithName(name:string) {
|
||||
const baseResourceList = new BaseResourceList(this.self());
|
||||
|
||||
return baseResourceList.resourceTable().sortableTable().rowElementWithName(name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import { ConfigMapPagePo } from '@/cypress/e2e/po/pages/explorer/config-map.po';
|
||||
import ConfigMapPo from '@/cypress/e2e/po/components/storage/config-map.po';
|
||||
|
||||
describe('ConfigMap', () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('creates a configmap and displays it in the list', () => {
|
||||
const expectedValue = `# Sample XPlanManagerAPI Configuration (if this comment is longer than 80 characters, the output should remain the same)
|
||||
|
||||
apiUrl=https://example.com/xplan-api-manager
|
||||
contactEmailAddress=contact@example.com
|
||||
termsOfServiceUrl=https://example.com/terms
|
||||
documentationUrl=https://example.com/docs
|
||||
wmsUrl=https://example.com/xplan-wms/services
|
||||
skipSemantic=false
|
||||
skipGeometric=true`;
|
||||
|
||||
// Visit the main menu and select the 'local' cluster
|
||||
// Navigate to Service Discovery => ConfigMaps
|
||||
const configMapPage = new ConfigMapPagePo('local');
|
||||
|
||||
configMapPage.goTo();
|
||||
|
||||
// Click on Create
|
||||
configMapPage.clickCreate();
|
||||
|
||||
// Enter ConfigMap name
|
||||
// Enter ConfigMap key
|
||||
// Enter ConfigMap value
|
||||
// Enter ConfigMap description
|
||||
const configMapPo = new ConfigMapPo();
|
||||
|
||||
configMapPo.nameInput().set('custom-config-map');
|
||||
configMapPo.keyInput().set('managerApiConfiguration.properties');
|
||||
configMapPo.valueInput().set(expectedValue);
|
||||
configMapPo.descriptionInput().set('Custom Config Map Description');
|
||||
|
||||
// Click on Create
|
||||
configMapPo.saveCreateForm().click();
|
||||
|
||||
// Check if the ConfigMap is created successfully
|
||||
configMapPage.listElementWithName('custom-config-map').should('exist');
|
||||
|
||||
// Navigate back to the edit page
|
||||
configMapPage.listElementWithName('custom-config-map')
|
||||
.find(`button[data-testid="sortable-table-0-action-button"]`)
|
||||
.click()
|
||||
.get(`li[data-testid="action-menu-0-item"]`)
|
||||
.click();
|
||||
|
||||
// Assert the current value yaml dumps will append a newline at the end
|
||||
configMapPo.valueInput().value().should('eq', `${ expectedValue }\n`);
|
||||
});
|
||||
});
|
||||
|
|
@ -644,6 +644,7 @@ export default {
|
|||
:clearable="false"
|
||||
:taggable="keyTaggable"
|
||||
:options="calculateOptions(row[keyName])"
|
||||
:data-testid="`select-kv-item-key-${i}`"
|
||||
@input="queueUpdate"
|
||||
/>
|
||||
<input
|
||||
|
|
@ -652,6 +653,7 @@ export default {
|
|||
v-model="row[keyName]"
|
||||
:disabled="isView || disabled || !keyEditable || isProtected(row.key)"
|
||||
:placeholder="keyPlaceholder"
|
||||
:data-testid="`input-kv-item-key-${i}`"
|
||||
@input="queueUpdate"
|
||||
@paste="onPaste(i, $event)"
|
||||
>
|
||||
|
|
@ -661,6 +663,7 @@ export default {
|
|||
<!-- Value -->
|
||||
<div
|
||||
:key="i+'value'"
|
||||
:data-testid="`kv-item-value-${i}`"
|
||||
class="kv-item value"
|
||||
>
|
||||
<slot
|
||||
|
|
|
|||
Loading…
Reference in New Issue