Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
**/webapp/localService/mockdata/*.js
**/webapp/localService/mockdata/*.js.*

# UI5 middlewares
**/.ui5-tooling-modules/**

# CAP cap-fe-ts-sample-node
_out
*.db
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The frontend was created using the SAP Fiori Tools for SAP Business Application
# What does the repo contain?

- Generate TypeScript types based on the service metadata document ([odata2ts](https://www.npmjs.com/package/@odata2ts/odata2ts))
- Unit tests ([QUnit](https://qunitjs.com/))
- Unit tests ([QUnit](https://qunitjs.com/)) including up-to-date [Sinon](https://www.npmjs.com/package/sinon) version for mocking (integrated with [ui5-tooling-modules](https://www.npmjs.com/package/ui5-tooling-modules))
- Integration tests ([OPA5](https://sapui5.hana.ondemand.com/#/api/sap.ui.test.Opa5), [sap.fe.test](https://sapui5.hana.ondemand.com/sdk/#/api/sap.fe.test))
- Test runner for Unit- and Integration tests including code coverage and test coverage reporting ([ui5-test-runner](https://www.npmjs.com/package/ui5-test-runner))
- End-to-End tests ([WDI5](https://github.com/ui5-community/wdi5), [wdio-timeline-reporter](https://www.npmjs.com/package/wdio-timeline-reporter), [sap.fe.test](https://sapui5.hana.ondemand.com/sdk/#/api/sap.fe.test))
Expand Down
4 changes: 3 additions & 1 deletion app/samples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"cap-fe-ts-sample-node": "file:../..",
"@sapui5/types": "1.136.2",
"@types/qunit": "2.5.4",
"@types/sinon": "2.3.0",
"@types/sinon": "17.0.3",
"sinon": "18.0.0",
"@ui5/cli": "4.0.19",
"@wdio/cli": "8.43.0",
"@wdio/junit-reporter": "8.43.0",
Expand All @@ -68,6 +69,7 @@
"ui5-test-runner": "5.8.1",
"ui5-task-check-eocp": "0.0.6",
"ui5-tooling-transpile": "3.8.0",
"ui5-tooling-modules": "3.30.2",
"wdio-timeline-reporter": "5.1.4",
"wdio-ui5-service": "2.1.1"
},
Expand Down
5 changes: 5 additions & 0 deletions app/samples/ui5-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ server:
debug: false
- name: ui5-middleware-livereload
afterMiddleware: compression
- name: ui5-tooling-modules-middleware
afterMiddleware: compression
configuration:
debug: false
- name: preview-middleware
afterMiddleware: ui5-middleware-livereload
configuration:
Expand All @@ -40,6 +44,7 @@ server:
- framework: "Testsuite"
- framework: "OPA5"
- framework: "Qunit"
init: "test/unit/init.js"
debug: false
- name: sap-fe-mockserver
mountPath: /
Expand Down
158 changes: 51 additions & 107 deletions app/samples/webapp/test/unit/controller/CommentsSectionControllerTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,135 +2,79 @@ import CommentsSectionController from "com/sap/cap/fe/ts/sample/ext/controller/C
import * as Sinon from "sinon";
import ResourceModel from "sap/ui/model/resource/ResourceModel";
import type ResourceBundle from "sap/base/i18n/ResourceBundle";
import type Event from "sap/ui/base/Event";
import type ExtensionAPI from "sap/fe/core/ExtensionAPI";
import Event from "sap/ui/base/Event";
import ExtensionAPI from "sap/fe/core/ExtensionAPI";
import { Message } from "com/sap/cap/fe/ts/sample/ext/utils/Constants";
import MessageBox from "sap/m/MessageBox";

type CommentsControllerStub = Pick<typeof CommentsSectionController["prototype"], "getExtensionAPI" | "getResourceBundle" | "onEditComment" | "onDeleteComment" | "onPostComment" | "_createEditCommentDialog"> & {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
overrides: Record<string, Record<string, (...args: any) => any>>
};

let controllerStub: CommentsControllerStub;
let setPropertyCalled = false;
let eventStub: Event;
import FeedListItem from "sap/m/FeedListItem";
import v4Context from "sap/ui/model/odata/v4/Context";

//@ts-expect-error: this is an instantiation of a controller for testing purposes
const commentsSectionController = new CommentsSectionController() as CommentsSectionController;
const sandbox = Sinon.createSandbox();
const resourceBundle = new ResourceModel({
bundleUrl: sap.ui.require.toUrl("com/sap/cap/fe/ts/sample") + "/i18n/i18n.properties"
}).getResourceBundle() as ResourceBundle
let fakeFeedListItem = new FeedListItem();
let fakeEvent = new Event("test", fakeFeedListItem, {}) as Event;

QUnit.module("Unit test for Task Management UI (Comments Section)", {
before: () => {
eventStub = {
getParameter: () => {
// only called for parameter 'value'
return "Test";
},
getSource: () => {
return {
getBindingContext: () => {
return {
getProperty: () => {
return "Draft";
},
setProperty: () => {
setPropertyCalled = true;
}
};
}
};
}
} as unknown as Event;

const resourceBundle = new ResourceModel({ bundleUrl: sap.ui.require.toUrl("com/sap/cap/fe/ts/sample") + "/i18n/i18n.properties" }).getResourceBundle() as ResourceBundle;

//@ts-expect-error: this is an instantiation of a controller stub for testing purposes
const commentsSectionControllerStub = new CommentsSectionController() as CommentsSectionController;

controllerStub = {
getExtensionAPI: function () {
return {
getEditFlow: () => {
return {
securedExecution: () => {
return new Promise(() => {
throw new Error("Test error");
});
}
};
}
} as unknown as ExtensionAPI;
},
getResourceBundle: function () {
return resourceBundle;
},
onEditComment: function (event: Event) {
commentsSectionControllerStub.onEditComment.call(this, event);
},
onDeleteComment: function (event: Event) {
return commentsSectionControllerStub.onDeleteComment.call(this, event);
},
onPostComment: function (event: Event) {
return commentsSectionControllerStub.onPostComment.call(this, event);
},
_createEditCommentDialog: function () {
return Promise.resolve();
},
overrides: {
editFlow: {
onBeforeSave: function () {
// @ts-expect-error getOverrides() unknown; will reflect 'overrides' of respective controller
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
commentsSectionControllerStub.getMetadata().getOverrides().editFlow.onBeforeSave.call(controllerStub);
}
}
}
};
},
beforeEach: () => {
// nothing
sandbox.stub(commentsSectionController, "getResourceBundle").returns(resourceBundle);
sandbox.stub(fakeEvent, "getParameter").resolves();
sandbox.stub(fakeEvent, "getSource").returns(fakeFeedListItem);
},
afterEach: () => {
// nothing
},
after: () => {
// nothing
sandbox.restore();
}
} as Hooks, undefined);
} satisfies Hooks);

// eslint-disable-next-line @typescript-eslint/no-misused-promises
QUnit.test("test onBeforeSave hook opens a message box of type success", async assert => {
const messageBoxStub = Sinon.stub(MessageBox, "success");
QUnit.test("Check that onBeforeSave callback opens a MessageBox of type success", async assert => {
const messageBoxSuccessStub = sandbox.stub(MessageBox, "success");

await controllerStub.overrides.editFlow.onBeforeSave();
// @ts-expect-error getOverrides() unknown; will reflect 'overrides' of respective controller
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
await commentsSectionController.getMetadata().getOverrides().editFlow.onBeforeSave.call(commentsSectionController);

const expectedText = controllerStub.getResourceBundle().getText("CallbackSuccess");
const actualText = messageBoxStub.getCall(0).args[0];
const expectedText = commentsSectionController.getResourceBundle().getText("CallbackSuccess");
const actualText = messageBoxSuccessStub.getCall(0).args[0];

assert.strictEqual(actualText, expectedText);

messageBoxStub.restore();
});

// eslint-disable-next-line @typescript-eslint/no-misused-promises
QUnit.test("Check that errors are displayed", async assert => {
const utilsStub = Sinon.stub(MessageBox, "error");

await controllerStub.onDeleteComment(eventStub);

const expectedText = controllerStub.getResourceBundle().getText(Message.error.GENERIC);
let actualText = utilsStub.getCall(0).args[0];

QUnit.test("Check that errors are being displayed when securedExecution fails", async assert => {
const messageBoxErrorStub = sandbox.stub(MessageBox, "error");
sandbox.stub(commentsSectionController, "getExtensionAPI").returns({
getEditFlow: () => {
return {
securedExecution: _fnFunction => Promise.reject(new Error("Test error"))
};
}
} as ExtensionAPI);

await commentsSectionController.onDeleteComment(fakeEvent);

const expectedText = commentsSectionController.getResourceBundle().getText(Message.error.GENERIC);
let actualText = messageBoxErrorStub.getCall(0).args[0];
assert.strictEqual(actualText, expectedText, "Error when deleting comment is displayed");

await controllerStub.onPostComment(eventStub);

actualText = utilsStub.getCall(1).args[0];
await commentsSectionController.onPostComment(fakeEvent);

actualText = messageBoxErrorStub.getCall(1).args[0];
assert.strictEqual(actualText, expectedText, "Error when posting comment is displayed");

utilsStub.restore();
});

QUnit.test("Check that 'type' property of comment not changed when already 'Draft'", assert => {
controllerStub.onEditComment(eventStub);
QUnit.test("Check that 'type' property of comment is not being changed in case of edit when already 'Draft'", assert => {
//@ts-expect-error: this is an instantiation of a context for testing purposes
const fakeBindingContext = new Context() as v4Context;
sandbox.stub(fakeFeedListItem, "getBindingContext").returns(fakeBindingContext);
sandbox.stub(fakeBindingContext, "getProperty").returns(commentsSectionController.getResourceBundle().getText("draft"));
const setPropertyStub = sandbox.stub(fakeBindingContext, "setProperty").resolves();
sandbox.stub(commentsSectionController, "_createEditCommentDialog").resolves();

commentsSectionController.onEditComment(fakeEvent);

assert.strictEqual(setPropertyCalled, false);
assert.strictEqual(setPropertyStub.getCalls().length, 0);
});
28 changes: 28 additions & 0 deletions app/samples/webapp/test/unit/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
sap.ui.loader.config({
map: {
'*': {
'sinon': null //default is sap/ui/thirdparty/sinon
}
},
shim: {
"sap/ui/qunit/qunit-junit": {
deps: ["sap/ui/thirdparty/qunit-2"]
},
"sap/ui/qunit/qunit-coverage": {
deps: ["sap/ui/thirdparty/qunit-2"]
}
}
});

window.QUnit = Object.assign({}, window.QUnit, { config: { autostart: false } });

sap.ui.require([
"sap/ui/thirdparty/qunit-2",
"sap/ui/qunit/qunit-junit",
"sap/ui/qunit/qunit-coverage"
], function (QUnit) {
'use strict';
sap.ui.require(["com/sap/cap/fe/ts/sample/test/unit/controller/CommentsSectionControllerTest"], function() {
QUnit.start();
});
});
Loading