Skip to content

Commit 4d7dea5

Browse files
authored
[UIA-1018] Release of version 9.6.0 (#59)
* [UIA-1018] Release module version 9.6.0 * [UIA-1018] Release module version 9.6.0
1 parent a32ed74 commit 4d7dea5

382 files changed

Lines changed: 16064 additions & 22620 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
66

77
## [Unreleased]
88

9+
## [9.6.0] - 2025-03-20
10+
11+
### Added:
12+
- We introduced a modernized and responsive UI for the unit test overview page.
13+
- We added filters to enable filtering based on test results.
14+
- We introduced a test execution timeline. You can view all the activities in the order that they were executed in. Activities include start, reported steps, assertions, exceptions and end result.
15+
- We now automatically discover added/removed unit tests. It is no longer required to reset/refresh the unit test overview.
16+
- We now support evaluation of multiple assertions in the same unit test. Optionally, the unit test microflow execution will continue so you can still verify the result of other assertions. Note that a failed assertion will always result in a failed test.
17+
- We now disable the unit testing module by default for any other environment other than local development. If you want to run unit tests in a deployed environment, set the UnitTesting.Enabled constant to true. We recommend to keep this constant set to false for production environments.
18+
- We exposed a new microflow activity “Assert using expression“ in the microflow toolbox. This action can be used to add a name to your assertion and configure whether or not to stop on failure.
19+
- We exposed the “Report step“ activity in the microflow toolbox. Use this action to track key steps of the test execution.
20+
- We introduced a new optional unit test parameter “UnitTestContext”. During test execution, a UnitTestContext object will be passed to this parameter. You can use this object to get access to the name of the test and any assertion results. This can be useful when you have multiple assertions in a single test and want to use the outcome of previous assertions in your test logic.
21+
- We added Accordion widget as a dependency, compatible with v2.3.4
22+
- We updated Atlas Core module compatibility to v3.12.5
23+
- We added Atlas Web Content module as a dependency, compatible with v3.4.2
24+
- We added Data Widgets module as a dependency, compatible with v2.27.3
25+
926
## [9.5.2] - 2024-10-15
1027

1128
### Fixed:

dist/UnitTesting_9.6.0.mpk

3.29 MB
Binary file not shown.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.mendix.UnitTesting</groupId>
88
<artifactId>UnitTesting</artifactId>
9-
<version>9.5.2</version>
9+
<version>9.6.0</version>
1010

1111
<repositories>
1212
<repository>

src/UnitTesting.mpr

10.8 MB
Binary file not shown.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// This file was generated by Mendix Studio Pro.
2+
//
3+
// WARNING: Only the following code will be retained when actions are regenerated:
4+
// - the import list
5+
// - the code between BEGIN USER CODE and END USER CODE
6+
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
7+
// Other code you write will be lost the next time you deploy the project.
8+
import "mx-global";
9+
import { Big } from "big.js";
10+
import { utils, writeFileXLSX } from './xlsx-export-tools.js';
11+
12+
// BEGIN EXTRA CODE
13+
// END EXTRA CODE
14+
15+
/**
16+
* @param {string} datagridName
17+
* @param {string} fileName
18+
* @param {string} sheetName
19+
* @param {boolean} includeColumnHeaders
20+
* @param {Big} chunkSize - The number of items fetched and exported per request.
21+
* @returns {Promise.<boolean>}
22+
*/
23+
export async function Export_To_Excel(datagridName, fileName, sheetName, includeColumnHeaders, chunkSize) {
24+
// BEGIN USER CODE
25+
if (!fileName || !datagridName || !sheetName) {
26+
return false;
27+
}
28+
29+
const REGISTRY_NAME = "com.mendix.widgets.web.datagrid.export";
30+
const registry = window[REGISTRY_NAME];
31+
const controller = registry.get(datagridName);
32+
33+
if (controller === undefined) {
34+
return false;
35+
}
36+
37+
return new Promise((resolve) => {
38+
function handler(req) {
39+
let worksheet;
40+
let headers;
41+
42+
req.on("headers", (hds) => {
43+
headers = hds.map(header => header.name);
44+
if (includeColumnHeaders) {
45+
worksheet = utils.aoa_to_sheet([headers]);
46+
}
47+
});
48+
49+
req.on("data", (data) => {
50+
if (worksheet === undefined) {
51+
worksheet = utils.aoa_to_sheet(data)
52+
} else {
53+
utils.sheet_add_aoa(worksheet, data, { origin: -1 });
54+
}
55+
});
56+
57+
req.on("end", () => {
58+
if (worksheet) {
59+
// Set character width for each column
60+
// https://docs.sheetjs.com/docs/csf/sheet#worksheet-object
61+
worksheet["!cols"] = headers.map(header => ({
62+
wch: header.length + 10
63+
}));
64+
const workbook = utils.book_new();
65+
utils.book_append_sheet(workbook, worksheet, sheetName === "" ? "Data" : sheetName);
66+
writeFileXLSX(workbook, `${fileName}.xlsx`);
67+
resolve(true);
68+
} else {
69+
resolve(false);
70+
}
71+
});
72+
73+
req.on("abort", () => resolve(false));
74+
}
75+
76+
controller.exportData(handler, {
77+
withHeaders: true,
78+
limit: chunkSize.toNumber()
79+
})
80+
});
81+
// END USER CODE
82+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This file was generated by Mendix Studio Pro.
2+
//
3+
// WARNING: Only the following code will be retained when actions are regenerated:
4+
// - the import list
5+
// - the code between BEGIN USER CODE and END USER CODE
6+
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
7+
// Other code you write will be lost the next time you deploy the project.
8+
import { Big } from "big.js";
9+
import "mx-global";
10+
11+
// BEGIN EXTRA CODE
12+
// END EXTRA CODE
13+
14+
/**
15+
* @param {string} targetName - Name of the widget for which filters should be reset. Valid targets are: Data grid 2, Gallery. You can find filter name in widget settings in the "Common" group (Properties>Common>Name).
16+
* @param {boolean} setToDefault - Set to default value. If true, filter will be set to its default value, otherwise it will be set to empty.
17+
* @returns {Promise.<void>}
18+
*/
19+
export async function Reset_All_Filters(targetName, setToDefault) {
20+
// BEGIN USER CODE
21+
const plugin = window["com.mendix.widgets.web.plugin.externalEvents"];
22+
if (plugin) {
23+
plugin.emit(targetName, "reset.filters", setToDefault);
24+
}
25+
// END USER CODE
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This file was generated by Mendix Studio Pro.
2+
//
3+
// WARNING: Only the following code will be retained when actions are regenerated:
4+
// - the import list
5+
// - the code between BEGIN USER CODE and END USER CODE
6+
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
7+
// Other code you write will be lost the next time you deploy the project.
8+
import "mx-global";
9+
import { Big } from "big.js";
10+
11+
// BEGIN EXTRA CODE
12+
// END EXTRA CODE
13+
14+
/**
15+
* @param {string} targetName - Name of the filter to reset. Valid targets are: Number filter, Date filter, Text filter, Drop-down filter. You can find filter name in widget settings in the "Common" group (Properties>Common>Name).
16+
* @param {boolean} setToDefault - Set to default value. If true, filter will be set to its default value, otherwise it will be set to empty.
17+
* @returns {Promise.<void>}
18+
*/
19+
export async function Reset_Filter(targetName, setToDefault) {
20+
// BEGIN USER CODE
21+
const plugin = window["com.mendix.widgets.web.plugin.externalEvents"];
22+
if (plugin) {
23+
plugin.emit(targetName, "reset.value", setToDefault);
24+
}
25+
// END USER CODE
26+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// This file was generated by Mendix Studio Pro.
2+
//
3+
// WARNING: Only the following code will be retained when actions are regenerated:
4+
// - the import list
5+
// - the code between BEGIN USER CODE and END USER CODE
6+
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
7+
// Other code you write will be lost the next time you deploy the project.
8+
import "mx-global";
9+
import { Big } from "big.js";
10+
11+
// BEGIN EXTRA CODE
12+
// END EXTRA CODE
13+
14+
/**
15+
* @param {string} targetName - Name of the filter to set. Valid targets are: Number filter, Date filter, Text filter, Drop-down filter. You can find filter name in widget settings in the "Common" group (Properties>Common>Name).
16+
* @param {boolean} useDefaultValue - Determine the use of default value provided by the filter component itself.
17+
If true, "Value" section will be ignored
18+
* @param {"DataWidgets.Filter_Operators.contains"|"DataWidgets.Filter_Operators.startsWith"|"DataWidgets.Filter_Operators.endsWith"|"DataWidgets.Filter_Operators.between"|"DataWidgets.Filter_Operators.greater"|"DataWidgets.Filter_Operators.greaterEqual"|"DataWidgets.Filter_Operators.equal"|"DataWidgets.Filter_Operators.notEqual"|"DataWidgets.Filter_Operators.smaller"|"DataWidgets.Filter_Operators.smallerEqual"|"DataWidgets.Filter_Operators.empty"|"DataWidgets.Filter_Operators.notEmpty"} operators - Selected operators value. If filter has operators, this value will be applied.
19+
* @param {string} stringValue - Value set for dropdown filter or text filter. Choose empty if not use.
20+
* @param {Big} numberValue - Number value for number filter. Choose empty if not use.
21+
* @param {Date} dateTimeValue - Date time value for date filter, can also be use as "start date". Choose empty if not use.
22+
* @param {Date} dateTimeValue_2 - End date time value for range filter. Choose empty if not use.
23+
* @returns {Promise.<void>}
24+
*/
25+
export async function Set_Filter(targetName, useDefaultValue, operators, stringValue, numberValue, dateTimeValue, dateTimeValue_2) {
26+
// BEGIN USER CODE
27+
const plugin = window["com.mendix.widgets.web.plugin.externalEvents"];
28+
if (plugin) {
29+
plugin.emit(targetName, "set.value", useDefaultValue, {
30+
operators, stringValue, numberValue, dateTimeValue, dateTimeValue2: dateTimeValue_2
31+
});
32+
}
33+
// END USER CODE
34+
}

src/javascriptsource/datawidgets/actions/xlsx-export-tools.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// This file was generated by Mendix Studio Pro.
2+
//
3+
// WARNING: Only the following code will be retained when actions are regenerated:
4+
// - the import list
5+
// - the code between BEGIN USER CODE and END USER CODE
6+
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
7+
// Other code you write will be lost the next time you deploy the project.
8+
import "mx-global";
9+
import { Big } from "big.js";
10+
import AsyncStorage from '@react-native-community/async-storage';
11+
12+
// BEGIN EXTRA CODE
13+
// END EXTRA CODE
14+
15+
/**
16+
* What does this JavaScript action do?
17+
*
18+
* Get locally stored JSON object stored in clients internet browser. Identified by a unique key. Can be accessed by the GetStorageItemObject action. Please note that users can clear the device storage.
19+
* @param {string} key - This field is required.
20+
* @param {string} entity - This field is required.
21+
* @returns {Promise.<MxObject>}
22+
*/
23+
export async function GetStorageItemObject(key, entity) {
24+
// BEGIN USER CODE
25+
if (!key) {
26+
return Promise.reject(new Error("Input parameter 'Key' is required"));
27+
}
28+
if (!entity) {
29+
return Promise.reject(new Error("Input parameter 'Entity' is required"));
30+
}
31+
return getItem(key).then(result => {
32+
if (result === null) {
33+
return Promise.reject(new Error(`Storage item '${key}' does not exist`));
34+
}
35+
const value = JSON.parse(result);
36+
return getOrCreateMxObject(entity, value).then(newObject => {
37+
const newValue = serializeMxObject(newObject);
38+
return setItem(key, JSON.stringify(newValue)).then(() => newObject);
39+
});
40+
});
41+
function getItem(key) {
42+
if (navigator && navigator.product === "ReactNative") {
43+
return AsyncStorage.getItem(key);
44+
}
45+
if (window) {
46+
const value = window.localStorage.getItem(key);
47+
return Promise.resolve(value);
48+
}
49+
return Promise.reject(new Error("No storage API available"));
50+
}
51+
function setItem(key, value) {
52+
if (navigator && navigator.product === "ReactNative") {
53+
return AsyncStorage.setItem(key, value);
54+
}
55+
if (window) {
56+
window.localStorage.setItem(key, value);
57+
return Promise.resolve();
58+
}
59+
return Promise.reject(new Error("No storage API available"));
60+
}
61+
function getOrCreateMxObject(entity, value) {
62+
return getMxObject(value.guid).then(existingObject => {
63+
if (existingObject) {
64+
return existingObject;
65+
}
66+
else {
67+
return createMxObject(entity, value);
68+
}
69+
});
70+
}
71+
function getMxObject(guid) {
72+
return new Promise((resolve, reject) => {
73+
mx.data.get({
74+
guid,
75+
callback: mxObject => resolve(mxObject),
76+
error: error => reject(error)
77+
});
78+
});
79+
}
80+
function createMxObject(entity, value) {
81+
return new Promise((resolve, reject) => {
82+
mx.data.create({
83+
entity,
84+
callback: mxObject => {
85+
Object.keys(value)
86+
.filter(attribute => attribute !== "guid")
87+
.forEach(attributeName => {
88+
const attributeValue = value[attributeName];
89+
mxObject.set(attributeName, attributeValue);
90+
});
91+
resolve(mxObject);
92+
},
93+
error: () => reject(new Error(`Could not create '${entity}' object`))
94+
});
95+
});
96+
}
97+
function serializeMxObject(object) {
98+
return object.getAttributes().reduce((accumulator, attributeName) => {
99+
accumulator[attributeName] = object.get(attributeName);
100+
return accumulator;
101+
}, { guid: object.getGuid() });
102+
}
103+
// END USER CODE
104+
}

0 commit comments

Comments
 (0)