Skip to content

Commit

Permalink
Merge branch 'main' into 3826-luigi-compound-container-cascading-context
Browse files Browse the repository at this point in the history
  • Loading branch information
walmazacn authored Jul 25, 2024
2 parents 0c3af94 + 47e51e5 commit ee27876
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 85 deletions.
64 changes: 37 additions & 27 deletions client-frameworks-support/testing-utilities/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,19 @@ describe('Another test using cypress', () => {
cy.get('.pathExists').click().then(() => {
luigiMockUtil.mockPathExists('/test', false);
});
cy.getAllSessionStorage().then((result: any) => {
expect(result).to.deep.equal({
"http://localhost:4200": {
luigiMockData: '{"pathExists":{"/test":false}}'
},
});
})
cy.getAllSessionStorage().then((storage: any) => {
const result = luigiMockUtil.getCleanSessionStorageData(storage);

expect(result).to.contains(luigiMockUtil.getMockedPathExistsOutput('/test', false));
});
});

it('should mock context update', () => {
const visualizationContainerId = luigiMockUtil.getVisualizationContainerId();
const context = {ctxKey: 'ctxValue'};

luigiMockUtil.mockContext(context);
cy.get('#luigi-debug-vis-cnt').contains('{"msg":"luigi.get-context","context":{"ctxKey":"ctxValue"}}');
cy.get('#' + visualizationContainerId).contains(luigiMockUtil.getMockedContextOutput(context));
});
});
```
Expand All @@ -113,22 +112,25 @@ describe('Another test using nightwatch', function () {
await browser.expect.element('.pathExists').to.be.present;
await browser.element('.pathExists').click().then(() => {
luigiMockUtil.mockPathExists('/test', false);
browser.execute(() => window.sessionStorage.getItem('luigiMockData'), [], function (result) {
expect(result.value).to.contains('{"pathExists":{"/test":false}}');
browser.execute(() => window.sessionStorage, [], function (storage) {
const result = luigiMockUtil.getCleanSessionStorageData(storage.value);

expect(result).to.contains(luigiMockUtil.getMockedPathExistsOutput('/test', false));
});
});
});

it('should mock context update', async () => {
const visualizationContainerId = luigiMockUtil.getVisualizationContainerId();
const context = {ctxKey: 'ctxValue'};

await luigiMockUtil.mockContext(context);
// Wait until '#luigi-debug-vis-cnt' element is present
await browser.waitForElementPresent('#luigi-debug-vis-cnt', undefined, undefined, false, () => {
const wrapper = browser.expect.element('#luigi-debug-vis-cnt');
// Wait until Luigi visualization container is present
await browser.waitForElementPresent('#' + visualizationContainerId, undefined, undefined, false, () => {
const wrapper = browser.expect.element('#' + visualizationContainerId);

wrapper.to.be.present;
wrapper.text.to.contains('{"msg":"luigi.get-context","context":{"ctxKey":"ctxValue"}}');
wrapper.text.to.contains(luigiMockUtil.getMockedContextOutput(context));
});
});

Expand Down Expand Up @@ -158,21 +160,23 @@ describe('Another test using webdriverio', () => {
// Wait until session storage item is set
await browser.setTimeout(defaultTimeout);

const result = await browser.execute(() => window.sessionStorage.getItem('luigiMockData'));
const storage = await browser.execute(() => window.sessionStorage);
const result = await luigiMockUtil.getCleanSessionStorageData(storage);

await expect(result).toEqual('{"pathExists":{"/test":false}}');
await expect(result).toContain(luigiMockUtil.getMockedPathExistsOutput('/test', false));
});

it('should mock context update', async () => {
luigiMockUtil = new LuigiMockUtil(browser);

const visualizationContainerId = luigiMockUtil.getVisualizationContainerId();
const context = {ctxKey: 'ctxValue'};

await browser.url(baseUrl);
await luigiMockUtil.mockContext(context);
// Wait until '#luigi-debug-vis-cnt' element is present
// Wait until Luigi visualization container is present
await browser.setTimeout(defaultTimeout);
await expect($('#luigi-debug-vis-cnt')).toHaveHTML(expect.stringContaining('{"msg":"luigi.get-context","context":{"ctxKey":"ctxValue"}}'));
await expect($('#' + visualizationContainerId)).toHaveHTML(expect.stringContaining(luigiMockUtil.getMockedContextOutput(context)));
});
});
```
Expand Down Expand Up @@ -221,33 +225,39 @@ describe('Another test using puppeteer ->', () => {
// Wait until session storage item is set
await new Promise(resolve => setTimeout(resolve, 500));

const result = await page.evaluate(() => window.sessionStorage.getItem('luigiMockData'));
const storage = await page.evaluate(() => JSON.stringify(window.sessionStorage));
const result = await luigiMockUtil.getCleanSessionStorageData(storage);

await expect(result).toContain('{"pathExists":{"/test":false}}');
await expect(result).toContain(luigiMockUtil.getMockedPathExistsOutput('/test', false));
});
});
});

it('should mock context update', async () => {
const visualizationContainerId = luigiMockUtil.getVisualizationContainerId();
const context = {ctxKey: 'ctxValue'};

await luigiMockUtil.mockContext(context);
// Wait until '#luigi-debug-vis-cnt' element is present
await page.waitForSelector('#luigi-debug-vis-cnt').then(async () => {
// Wait until Luigi visualization container is present
await page.waitForSelector('#' + visualizationContainerId).then(async () => {
const result = await page
.locator('#luigi-debug-vis-cnt div:nth-child(1)')
.locator(`#${visualizationContainerId} div:nth-child(1)`)
.map(div => div.innerText)
.wait();

expect(result).toContain('{"msg":"luigi.get-context","context":{"ctxKey":"ctxValue"}}');
expect(result).toContain(luigiMockUtil.getMockedContextOutput(context));
});
});
});
```
#### Functions provided
- **mockContext**: Mocks the context by sending Luigi context messages with the desired mocked context as parameter.
- **mockPathExists**: This method serves as a mock for the Luigi Client `pathExists()` function. It is used in e2e tests when component being tested utilizes a call to `LuigiClient.linkManager().pathExists()`
- **mockPathExists**: This method serves as a mock for the Luigi Client `pathExists()` function. It is used in e2e tests when component being tested utilizes a call to `LuigiClient.linkManager().pathExists()`.
- **modalOpenedWithTitle**: Checks on the printed DOM Luigi message responses for a modal with given title being opened. In such a case, a message would be printed containing a `modal.title`. Returns `false` if such element was not found.
- **getMSG**: Return list of messages, representing message elements added in the DOM for testing.
- **parseLuigiMockedMessages**: Parses the elements added by LuigiMockModule into the DOM and assigns them to the local messages variable
- **getMockedContextOutput**: Returns output of 'mockContext' method with given data.
- **getMockedPathExistsOutput**: Returns output of 'mockPathExists' method with given arguments.
- **getCleanSessionStorageData**: Returns parsed session storage data used for testing.
- **getVisualizationContainerId**: Returns ID of Luigi visualization container added in the DOM for testing.
- **getMSG**: Returns list of messages, representing message elements added in the DOM for testing.
- **parseLuigiMockedMessages**: Parses the elements added by LuigiMockModule into the DOM and assigns them to the local messages variable.
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,20 @@ export class LuigiMockEngine {
* which holds data that is useful for e2e testing.
*/
public static visualize(data: string): void {
let luigiVisualizationContainer: Element | null = document.querySelector('#luigi-debug-vis-cnt');
const visualizationContainerId = 'luigi-debug-vis-cnt';
const dataWrapper: HTMLDivElement = document.createElement('div');
let luigiVisualizationContainer: Element | null = document.querySelector('#' + visualizationContainerId);

// Construct element structure if not already constructed
if (!luigiVisualizationContainer) {
luigiVisualizationContainer = document.createElement('div');
luigiVisualizationContainer.setAttribute('id', 'luigi-debug-vis-cnt');
luigiVisualizationContainer.setAttribute('id', visualizationContainerId);
// Hide the added DOM element to avoid interferring/overlapping with other elements during testing.
luigiVisualizationContainer.setAttribute('style', 'display:none');
document.body.appendChild(luigiVisualizationContainer);
}
const line: HTMLDivElement = document.createElement('div');
line.textContent = data;
luigiVisualizationContainer.appendChild(line);

dataWrapper.textContent = data;
luigiVisualizationContainer.appendChild(dataWrapper);
}
}
76 changes: 61 additions & 15 deletions client-frameworks-support/testing-utilities/src/luigi-mock-util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export class LuigiMockUtil {
private sessionStorageItemName = 'luigiMockData';
private visualizationContainerId = 'luigi-debug-vis-cnt';
private messages: any[];
private browser: any;
private win: any;
Expand All @@ -9,22 +11,14 @@ export class LuigiMockUtil {
this.win = win;
}

/**
* Returns the global window object.
* @returns the glboal win object
*/
private getGlobalThis(): any {
return this.win || globalThis;
}

/**
* Parses the elements added by LuigiMockModule into the DOM and assigns them to the local this.messages variable
* @returns {Promise<void>} - A Promise that resolves when parsing is complete.
*/
async parseLuigiMockedMessages(): Promise<void> {
const window = this.getGlobalThis();
const getTextNodeValues = (): any[] => {
const debugCtn = window.getElementById('luigi-debug-vis-cnt');
const debugCtn = window.getElementById(this.visualizationContainerId);

return Array.from(debugCtn?.childNodes || []).map((item: any) => item.textContent || '');
};
Expand Down Expand Up @@ -64,7 +58,7 @@ export class LuigiMockUtil {
* Mocks the context by sending luigi context messages with the desired mocked context as parameter.
* @param mockContext an object representing the context to be mocked
*/
mockContext = (mockContext: Record<string, any>): void => {
mockContext(mockContext: Record<string, any>): void {
const window = this.getGlobalThis();
const postMessageToLuigi = (context: Record<string, any>): Record<string, any> => {
window.postMessage({ msg: 'luigi.get-context', context }, '*');
Expand All @@ -90,7 +84,7 @@ export class LuigiMockUtil {
} catch (error) {
console.debug('Failed to mock context: ', error);
}
};
}

/**
* This method serves as a mock for the luigi client pathExists() function.
Expand All @@ -108,7 +102,7 @@ export class LuigiMockUtil {
* await mockPathExists('pathToCheck', false);
*
*/
mockPathExists = (path: string, exists: boolean): void => {
mockPathExists(path: string, exists: boolean): void {
const window = this.getGlobalThis();
const mockContext: Record<string, boolean | string> = { path, exists };
/**
Expand All @@ -126,7 +120,7 @@ export class LuigiMockUtil {
}
};

window.sessionStorage.setItem('luigiMockData', JSON.stringify(pathExistsMockData));
window.sessionStorage.setItem(this.sessionStorageItemName, JSON.stringify(pathExistsMockData));

return { ...pathExistsMockData, sessionItem: 'isStored' };
};
Expand All @@ -149,7 +143,7 @@ export class LuigiMockUtil {
} catch (error) {
console.debug('Failed to mock path exists: ', error);
}
};
}

/**
* Checks on the printed DOM Luigi message responses for a modal with given title
Expand Down Expand Up @@ -207,9 +201,61 @@ export class LuigiMockUtil {
}

/**
* Return list of messages, representing message elements added in the DOM for testing.
* Returns output of 'mockContext' method with given data.
* @param {Object} context - Object representing the context to be mocked.
* @returns {string} - Stringified output of 'mockContext' method.
*/
getMockedContextOutput(context: Record<string, any>): string {
return `{"msg":"luigi.get-context","context":${JSON.stringify(context)}}`;
}

/**
* Returns output of 'mockPathExists' method with given arguments.
* @param {string} path - The path for which mock data is to be set.
* @param {boolean} exists - Boolean indicating whether the path exists.
* @returns {string} - Stringified output of 'mockPathExists' method.
*/
getMockedPathExistsOutput(path: string, exists: boolean): string {
return JSON.stringify({ [this.sessionStorageItemName]: { pathExists: { [path]: exists } } }).slice(1, -1);
}

/**
* Returns parsed session storage data used for testing.
* @param {Object} data - Object or string representing the data to be cleaned.
* @returns {string} - Stringified session storage data.
*/
getCleanSessionStorageData(data: any): string {
if (typeof data === 'string') {
data = JSON.parse(data);
}

return JSON.stringify(data)
.replace(/\\/g, '')
.replace(/"{/g, '{')
.replace(/}"/g, '}');
}

/**
* Returns ID of Luigi visualization container added in the DOM for testing.
* @returns {string} - ID of Luigi visualization container.
*/
getVisualizationContainerId(): string {
return this.visualizationContainerId;
}

/**
* Returns list of messages, representing message elements added in the DOM for testing.
* @returns {Array} - Array of message elements.
*/
getMSG(): any[] {
return this.messages;
}

/**
* Returns the global window object.
* @returns the global win object
*/
private getGlobalThis(): any {
return this.win || globalThis;
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
let uxManager, linkManager;

describe('Luigi Mock Engine', () => {
const visualizationContainer = '[id^="luigi-debug-vis-cnt"]';

before(() => {
cy.visit('http://localhost:8181/');
});

/**
* Testing Luigi Client UX Manager features
*/
describe('Test Luigi Client UX Manager functionality', () => {
it('Check LuigiClient.uxManager().alert', () => {
cy.get('[id^=uxbutton1]').click();

cy.get('[id^="luigi-debug-vis-cnt"]')
cy.get(visualizationContainer)
.children()
.contains('"msg":"luigi.ux.alert.show"');
});

it('Check LuigiClient.uxManager().confirmModal', () => {
cy.get('[id^=uxbutton2]').click();

cy.get('[id^="luigi-debug-vis-cnt"]')
cy.get(visualizationContainer)
.children()
.contains('"msg":"luigi.ux.confirmationModal.show"');
});

it('Check LuigiClient.uxManager().loadIndicator', () => {
cy.get('[id^=uxbutton3]').click();

cy.get('[id^="luigi-debug-vis-cnt"]')
cy.get(visualizationContainer)
.children()
.contains('"msg":"luigi.show-loading-indicator"');
});

it('Check LuigiClient.uxManager().setCurrentLocale', () => {
cy.get('[id^=uxbutton4]').click();

cy.get('[id^="luigi-debug-vis-cnt"]')
cy.get(visualizationContainer)
.children()
.contains('"msg":"luigi.current-locale-changed"');
});
Expand All @@ -48,27 +49,27 @@ describe('Luigi Mock Engine', () => {
it('Check LuigiClient.linkManager().openAsModal', () => {
cy.get('[id^=button1]').click();

cy.get('[id^="luigi-debug-vis-cnt"]')
cy.get(visualizationContainer)
.children()
.contains('"msg":"luigi.navigate.ok"');
});

it('Check LuigiClient.linkManager().split', () => {
cy.get('[id^=button2]').click();

cy.get('[id^="luigi-debug-vis-cnt"]').contains('"msg":"luigi.navigate.ok"');
cy.get(visualizationContainer).contains('"msg":"luigi.navigate.ok"');
});

it('Check LuigiClient.linkManager().drawer', () => {
cy.get('[id^=button3]').click();

cy.get('[id^="luigi-debug-vis-cnt"]').contains('"msg":"luigi.navigate.ok"');
cy.get(visualizationContainer).contains('"msg":"luigi.navigate.ok"');
});

it('Check LuigiClient.linkManager().pathExists', () => {
cy.get('[id^=button4]').click();

cy.get('[id^="luigi-debug-vis-cnt"]').contains('"msg":"luigi.navigation.pathExists.answer"');
cy.get(visualizationContainer).contains('"msg":"luigi.navigation.pathExists.answer"');
});
});
});
Loading

0 comments on commit ee27876

Please sign in to comment.