Skip to content

Commit eabead9

Browse files
test: add new tests
1 parent 8453cfc commit eabead9

7 files changed

Lines changed: 304 additions & 0 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, expect, test } from 'react-native-harness';
2+
import { DevSettings } from 'mendix-native';
3+
4+
describe('DevSettings', () => {
5+
test('safe toggle APIs can be invoked repeatedly', () => {
6+
expect(DevSettings.setHotLoadingEnabled(true)).toBeUndefined();
7+
expect(DevSettings.setHotLoadingEnabled(false)).toBeUndefined();
8+
9+
expect(DevSettings.setProfilingEnabled(true)).toBeUndefined();
10+
expect(DevSettings.setProfilingEnabled(false)).toBeUndefined();
11+
12+
expect(DevSettings.setShakeToShowDevMenuEnabled(true)).toBeUndefined();
13+
expect(DevSettings.setShakeToShowDevMenuEnabled(false)).toBeUndefined();
14+
});
15+
16+
test('menu items can be registered without throwing', () => {
17+
expect(
18+
DevSettings.addMenuItem('Harness menu item', () => {})
19+
).toBeUndefined();
20+
});
21+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { beforeEach, describe, expect, test } from 'react-native-harness';
2+
import { NativeDownloadHandler, NativeFileSystem } from 'mendix-native';
3+
4+
const downloadPath = NativeFileSystem.relativeToDocumentsAbsolutePath(
5+
'downloads/invalid-url.txt'
6+
);
7+
8+
describe('NativeDownloadHandler', () => {
9+
beforeEach(async () => {
10+
try {
11+
await NativeFileSystem.remove(downloadPath);
12+
} catch {
13+
// Cleanup is best-effort.
14+
}
15+
});
16+
17+
test('rejects malformed URLs without creating a destination file', async () => {
18+
const config = {
19+
connectionTimeout: 25,
20+
mimeType: 'text/plain',
21+
};
22+
23+
await expect(
24+
NativeDownloadHandler.download(
25+
'://definitely-invalid-url',
26+
downloadPath,
27+
config
28+
)
29+
).rejects.toBeDefined();
30+
31+
expect(await NativeFileSystem.fileExists(downloadPath)).toBe(false);
32+
});
33+
34+
test('does not mutate the config object while rejecting invalid downloads', async () => {
35+
const config = {
36+
connectionTimeout: 10,
37+
mimeType: 'application/json',
38+
};
39+
const originalConfig = { ...config };
40+
41+
await expect(
42+
NativeDownloadHandler.download('://still-invalid', downloadPath, config)
43+
).rejects.toBeDefined();
44+
45+
expect(config).toEqual(originalConfig);
46+
});
47+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { beforeEach, describe, expect, test } from 'react-native-harness';
2+
import { MxEncryption, RNMendixEncryptedStorage } from 'mendix-native';
3+
4+
describe('MxEncryption', () => {
5+
beforeEach(async () => {
6+
await MxEncryption.clear();
7+
});
8+
9+
test('stores and retrieves values through the modern API', async () => {
10+
await MxEncryption.setItem('modern-key', 'modern-value');
11+
12+
await expect(MxEncryption.getItem('modern-key')).resolves.toBe(
13+
'modern-value'
14+
);
15+
});
16+
17+
test('shares the same backing store as the legacy encrypted storage API', async () => {
18+
await MxEncryption.setItem('shared-key', 'set-by-modern');
19+
await expect(RNMendixEncryptedStorage.getItem('shared-key')).resolves.toBe(
20+
'set-by-modern'
21+
);
22+
23+
await RNMendixEncryptedStorage.setItem('shared-key', 'set-by-legacy');
24+
await expect(MxEncryption.getItem('shared-key')).resolves.toBe(
25+
'set-by-legacy'
26+
);
27+
});
28+
29+
test('removeItem is visible across both exported wrappers', async () => {
30+
await RNMendixEncryptedStorage.setItem('cross-remove-key', 'value');
31+
32+
await MxEncryption.removeItem('cross-remove-key');
33+
34+
await expect(
35+
RNMendixEncryptedStorage.getItem('cross-remove-key')
36+
).resolves.toBe(null);
37+
});
38+
39+
test('isEncrypted matches the legacy constant contract', () => {
40+
expect(MxEncryption.isEncrypted()).toBe(
41+
RNMendixEncryptedStorage.IS_ENCRYPTED
42+
);
43+
});
44+
});

example/__tests__/error.harness.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { describe, expect, test } from 'react-native-harness';
2+
import { NativeErrorHandler } from 'mendix-native';
3+
4+
describe('NativeErrorHandler', () => {
5+
test('accepts normalized stack frame payloads synchronously', () => {
6+
const stackTrace = [
7+
{
8+
column: 12,
9+
file: 'example/__tests__/error.harness.ts',
10+
lineNumber: 8,
11+
methodName: 'accepts normalized stack frame payloads synchronously',
12+
},
13+
] as any;
14+
15+
expect(() => {
16+
const result = NativeErrorHandler.handle(
17+
'Harness error contract check',
18+
stackTrace
19+
);
20+
21+
expect(result).toBeUndefined();
22+
}).not.toThrow();
23+
});
24+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, expect, test } from 'react-native-harness';
2+
import { onDownloadProgressEvent, onReloadWithStateEvent } from 'mendix-native';
3+
4+
describe('Module events', () => {
5+
test('download progress exposes a removable subscription', () => {
6+
let callbackCount = 0;
7+
8+
const subscription = onDownloadProgressEvent(() => {
9+
callbackCount += 1;
10+
});
11+
12+
expect(subscription).toBeDefined();
13+
expect(typeof subscription.remove).toBe('function');
14+
expect(callbackCount).toBe(0);
15+
16+
subscription.remove();
17+
});
18+
19+
test('reload state exposes a removable subscription', () => {
20+
let callbackCount = 0;
21+
22+
const subscription = onReloadWithStateEvent(() => {
23+
callbackCount += 1;
24+
});
25+
26+
expect(subscription).toBeDefined();
27+
expect(typeof subscription.remove).toBe('function');
28+
expect(callbackCount).toBe(0);
29+
30+
subscription.remove();
31+
});
32+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { describe, expect, test } from 'react-native-harness';
2+
import { NativeReloadHandler, onReloadWithStateEvent } from 'mendix-native';
3+
4+
describe('NativeReloadHandler', () => {
5+
test('exposes async control methods and a removable state listener', () => {
6+
const subscription = onReloadWithStateEvent(() => {});
7+
8+
expect(typeof NativeReloadHandler.reload).toBe('function');
9+
expect(typeof NativeReloadHandler.exitApp).toBe('function');
10+
expect(typeof subscription.remove).toBe('function');
11+
12+
subscription.remove();
13+
});
14+
});
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import AsyncStorage from '@react-native-async-storage/async-storage';
2+
import { open } from '@op-engineering/op-sqlite';
3+
import { beforeEach, describe, expect, test } from 'react-native-harness';
4+
import { Storage } from 'mendix-native';
5+
6+
const DB_NAME = 'storage-harness.sqlite';
7+
const TABLE_NAME = 'storage_harness_records';
8+
9+
async function seedDatabase(): Promise<void> {
10+
const db = open({ name: DB_NAME });
11+
12+
await db.execute(
13+
`CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (id INTEGER PRIMARY KEY NOT NULL, value TEXT NOT NULL)`
14+
);
15+
await db.execute(`DELETE FROM ${TABLE_NAME}`);
16+
await db.execute(`INSERT INTO ${TABLE_NAME} (value) VALUES (?)`, ['fixture']);
17+
18+
db.close();
19+
}
20+
21+
async function getRowCount(): Promise<number> {
22+
const db = open({ name: DB_NAME });
23+
const result = await db.execute(
24+
`SELECT COUNT(*) AS count FROM ${TABLE_NAME}`
25+
);
26+
const rowCount = Number(result.rows?.[0]?.count ?? 0);
27+
28+
db.close();
29+
30+
return rowCount;
31+
}
32+
33+
async function tableExists(): Promise<boolean> {
34+
const db = open({ name: DB_NAME });
35+
const result = await db.execute(
36+
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",
37+
[TABLE_NAME]
38+
);
39+
const exists = result.rows.length > 0;
40+
41+
db.close();
42+
43+
return exists;
44+
}
45+
46+
describe('Storage', () => {
47+
beforeEach(async () => {
48+
await AsyncStorage.clear();
49+
50+
try {
51+
await Storage.closeDatabaseConnections();
52+
} catch {
53+
// Ignore cleanup failures so the real test can surface the error.
54+
}
55+
56+
try {
57+
await Storage.clearDatabases();
58+
} catch {
59+
// Ignore cleanup failures so the real test can surface the error.
60+
}
61+
});
62+
63+
describe('clearAsyncStorage', () => {
64+
test('removes previously persisted AsyncStorage keys', async () => {
65+
await AsyncStorage.setItem('storage-harness:key-1', 'value-1');
66+
await AsyncStorage.setItem('storage-harness:key-2', 'value-2');
67+
68+
await Storage.clearAsyncStorage();
69+
70+
const values = await AsyncStorage.multiGet([
71+
'storage-harness:key-1',
72+
'storage-harness:key-2',
73+
]);
74+
75+
expect(values).toEqual([
76+
['storage-harness:key-1', null],
77+
['storage-harness:key-2', null],
78+
]);
79+
});
80+
});
81+
82+
describe('closeDatabaseConnections', () => {
83+
test('closes active sqlite connections without deleting persisted data', async () => {
84+
const db = open({ name: DB_NAME });
85+
86+
await db.execute(
87+
`CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (id INTEGER PRIMARY KEY NOT NULL, value TEXT NOT NULL)`
88+
);
89+
await db.execute(`DELETE FROM ${TABLE_NAME}`);
90+
await db.execute(`INSERT INTO ${TABLE_NAME} (value) VALUES (?)`, [
91+
'fixture',
92+
]);
93+
94+
await Storage.closeDatabaseConnections();
95+
96+
expect(await getRowCount()).toBe(1);
97+
});
98+
});
99+
100+
describe('clearDatabases', () => {
101+
test('removes sqlite schema created through op-sqlite', async () => {
102+
await seedDatabase();
103+
expect(await tableExists()).toBe(true);
104+
105+
await Storage.clearDatabases();
106+
107+
expect(await tableExists()).toBe(false);
108+
});
109+
});
110+
111+
describe('clearAll', () => {
112+
test('clears AsyncStorage and sqlite state in one call', async () => {
113+
await AsyncStorage.setItem('storage-harness:combined', 'present');
114+
await seedDatabase();
115+
116+
await Storage.clearAll();
117+
118+
expect(await AsyncStorage.getItem('storage-harness:combined')).toBe(null);
119+
expect(await tableExists()).toBe(false);
120+
});
121+
});
122+
});

0 commit comments

Comments
 (0)