dazscript-framework 1.0.39 → 1.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -31
- package/babel.config.js +0 -1
- package/dist/scripts/icons.js +2 -2
- package/dist/scripts/install-generator.js +6 -2
- package/dist/scripts/integration.js +0 -3
- package/package.json +16 -18
- package/src/dialog/basic-dialog.test.ts +48 -0
- package/src/dialog/basic-dialog.ts +6 -2
- package/src/dialog/builders/list-view-builder.items.test.ts +78 -0
- package/src/helpers/action-helper.test.ts +28 -0
- package/src/helpers/action-helper.ts +7 -2
- package/src/helpers/file-helper.test.ts +74 -0
- package/src/helpers/file-helper.ts +12 -8
- package/src/scripts/integration.test.ts +9 -0
- package/webpack.config.js +2 -3
package/README.md
CHANGED
|
@@ -480,6 +480,20 @@ action({ text: 'My Dialog Script' }, () => {
|
|
|
480
480
|
});
|
|
481
481
|
```
|
|
482
482
|
|
|
483
|
+
#### 4. Prepare without opening modally
|
|
484
|
+
|
|
485
|
+
`prepare()` builds the dialog and returns its native `DzBasicDialog` without entering the modal event loop. Use it when code must inspect or configure the completed native dialog before showing it.
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
const dialog = new MyDialog(new MyModel());
|
|
489
|
+
const nativeDialog = dialog.prepare();
|
|
490
|
+
|
|
491
|
+
nativeDialog.minWidth = 600;
|
|
492
|
+
const accepted = nativeDialog.exec();
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Each call builds the dialog. After `prepare()`, execute the returned native dialog directly instead of calling `run()` or `ok()`, which would build it again.
|
|
496
|
+
|
|
483
497
|
---
|
|
484
498
|
|
|
485
499
|
### Observables
|
|
@@ -629,37 +643,7 @@ The `test/unit/` files are generated only when you run `dazscript init --unit-te
|
|
|
629
643
|
|
|
630
644
|
### Development & Publishing
|
|
631
645
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
#### Commit message conventions
|
|
635
|
-
|
|
636
|
-
| Prefix | Effect |
|
|
637
|
-
|---|---|
|
|
638
|
-
| `fix: ...` | Patch bump (`1.0.0` → `1.0.1`) |
|
|
639
|
-
| `feat: ...` | Minor bump (`1.0.0` → `1.1.0`) |
|
|
640
|
-
| `BREAKING CHANGE: ...` in commit body | Major bump (`1.0.0` → `2.0.0`) |
|
|
641
|
-
| No prefix | No version bump |
|
|
642
|
-
|
|
643
|
-
Examples:
|
|
644
|
-
```
|
|
645
|
-
fix: resolve layout overflow in group builder
|
|
646
|
-
feat: add tree view builder
|
|
647
|
-
feat: refactor action entrypoint
|
|
648
|
-
|
|
649
|
-
BREAKING CHANGE: action() now requires an explicit menuPath
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
#### Publishing
|
|
653
|
-
|
|
654
|
-
Every push to `master` automatically:
|
|
655
|
-
|
|
656
|
-
1. Analyzes commit messages since the last release
|
|
657
|
-
2. Updates the version in `package.json`
|
|
658
|
-
3. Builds the project
|
|
659
|
-
4. Creates a GitHub release with changelog
|
|
660
|
-
5. Publishes to npm
|
|
661
|
-
|
|
662
|
-
No manual steps required.
|
|
646
|
+
Every eligible push to `main` runs the npm publishing workflow. It installs from the lockfile, builds the package, increments the patch version, commits the version update with an annotated tag, and publishes the package to npm. Bot commits and commits containing `[skip ci]` do not start another release.
|
|
663
647
|
|
|
664
648
|
---
|
|
665
649
|
|
package/babel.config.js
CHANGED
|
@@ -18,7 +18,6 @@ module.exports = {
|
|
|
18
18
|
[require.resolve('@babel/plugin-proposal-decorators'), { version: 'legacy' }],
|
|
19
19
|
[require.resolve('@babel/plugin-transform-arrow-functions')],
|
|
20
20
|
require.resolve('@babel/plugin-transform-block-scoping'),
|
|
21
|
-
require.resolve('@babel/plugin-proposal-class-properties'),
|
|
22
21
|
require.resolve('@babel/plugin-transform-private-property-in-object'),
|
|
23
22
|
require.resolve('@babel/plugin-transform-private-methods'),
|
|
24
23
|
],
|
package/dist/scripts/icons.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const
|
|
5
|
+
const { globSync } = require('glob');
|
|
6
6
|
|
|
7
7
|
function copyFile(sourcePath, targetPath) {
|
|
8
8
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
@@ -14,7 +14,7 @@ function copyIcons(workdir, options) {
|
|
|
14
14
|
const sourceRoot = path.resolve(workdir, 'src');
|
|
15
15
|
const outDir = path.resolve(workdir, options.outDir || './out');
|
|
16
16
|
const pattern = path.join(sourceRoot, '**/*.png').replace(/\\/g, '/');
|
|
17
|
-
const files =
|
|
17
|
+
const files = globSync(pattern);
|
|
18
18
|
|
|
19
19
|
files.forEach((filePath) => {
|
|
20
20
|
const relativePath = path.relative(sourceRoot, filePath);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const ts = require('typescript');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const
|
|
4
|
+
const { globSync } = require('glob');
|
|
5
5
|
const { loadConfig } = require('./config-loader');
|
|
6
6
|
const { validateAppDataPath } = require('./app-data-path');
|
|
7
7
|
|
|
@@ -187,7 +187,11 @@ function findActionEntryFiles(workdir, options) {
|
|
|
187
187
|
const scriptsPath = options.scriptsPath.endsWith('/')
|
|
188
188
|
? options.scriptsPath
|
|
189
189
|
: `${options.scriptsPath}/`;
|
|
190
|
-
const matches =
|
|
190
|
+
const matches = globSync(`${scriptsPath}/**/*.dsa.ts`, { cwd: workdir })
|
|
191
|
+
.map((filePath) => {
|
|
192
|
+
const normalized = toPosix(filePath).replace(/^\.\//, '');
|
|
193
|
+
return `./${normalized}`;
|
|
194
|
+
});
|
|
191
195
|
|
|
192
196
|
return matches.filter((filePath) => {
|
|
193
197
|
const absolutePath = path.join(workdir, filePath);
|
|
@@ -197,7 +197,6 @@ function writeFile(filePath, content) {
|
|
|
197
197
|
|
|
198
198
|
const fixtureBuildDependencies = [
|
|
199
199
|
'@babel/core',
|
|
200
|
-
'@babel/plugin-proposal-class-properties',
|
|
201
200
|
'@babel/plugin-proposal-decorators',
|
|
202
201
|
'@babel/plugin-transform-arrow-functions',
|
|
203
202
|
'@babel/plugin-transform-block-scoping',
|
|
@@ -206,9 +205,7 @@ const fixtureBuildDependencies = [
|
|
|
206
205
|
'@babel/plugin-transform-private-property-in-object',
|
|
207
206
|
'@babel/preset-env',
|
|
208
207
|
'@babel/preset-typescript',
|
|
209
|
-
'babel-core',
|
|
210
208
|
'babel-loader',
|
|
211
|
-
'babel-plugin-transform-class-properties',
|
|
212
209
|
'babel-plugin-transform-typescript-metadata',
|
|
213
210
|
'glob',
|
|
214
211
|
'ts-loader',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dazscript-framework",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.41",
|
|
4
4
|
"author": "Freddy Diaz",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"description": "",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"installer": "node ./dist/scripts/cli.js installer --scripts-path ./src/examples --menu-path /DazScriptFramework",
|
|
15
15
|
"test": "vitest run",
|
|
16
16
|
"test:integration": "node ./dist/scripts/cli.js integration --fixture ./test/integration/fixtures/framework-integration.dsa.ts --require-content",
|
|
17
|
+
"test:integration:smoke": "node ./dist/scripts/cli.js integration --fixture ./test/integration/fixtures/framework-smoke.dsa.ts",
|
|
17
18
|
"test:watch": "vitest"
|
|
18
19
|
},
|
|
19
20
|
"bin": {
|
|
@@ -47,32 +48,29 @@
|
|
|
47
48
|
"dazscript-types": "^1.0.0"
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"@babel/core": "^7.
|
|
51
|
-
"@babel/plugin-proposal-
|
|
52
|
-
"@babel/plugin-
|
|
53
|
-
"@babel/plugin-transform-
|
|
54
|
-
"@babel/plugin-transform-
|
|
55
|
-
"@babel/plugin-transform-
|
|
56
|
-
"@babel/plugin-transform-private-
|
|
57
|
-
"@babel/
|
|
58
|
-
"@babel/preset-
|
|
59
|
-
"
|
|
60
|
-
"babel-core": "^6.26.3",
|
|
61
|
-
"babel-loader": "^9.1.3",
|
|
62
|
-
"babel-plugin-transform-class-properties": "^6.24.1",
|
|
51
|
+
"@babel/core": "^7.29.7",
|
|
52
|
+
"@babel/plugin-proposal-decorators": "^7.29.7",
|
|
53
|
+
"@babel/plugin-transform-arrow-functions": "^7.29.7",
|
|
54
|
+
"@babel/plugin-transform-block-scoping": "^7.29.7",
|
|
55
|
+
"@babel/plugin-transform-class-properties": "^7.29.7",
|
|
56
|
+
"@babel/plugin-transform-private-methods": "^7.29.7",
|
|
57
|
+
"@babel/plugin-transform-private-property-in-object": "^7.29.7",
|
|
58
|
+
"@babel/preset-env": "^7.29.7",
|
|
59
|
+
"@babel/preset-typescript": "^7.29.7",
|
|
60
|
+
"babel-loader": "^10.1.1",
|
|
63
61
|
"babel-plugin-transform-typescript-metadata": "^0.3.2",
|
|
64
|
-
"glob": "^
|
|
62
|
+
"glob": "^13.0.6",
|
|
65
63
|
"ts-file-parser": "^0.0.21",
|
|
66
|
-
"ts-loader": "^9.
|
|
64
|
+
"ts-loader": "^9.6.0",
|
|
67
65
|
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
|
68
66
|
"typescript": "^5.2.2",
|
|
69
|
-
"webpack": "^5.
|
|
67
|
+
"webpack": "^5.107.2"
|
|
70
68
|
},
|
|
71
69
|
"devDependencies": {
|
|
72
70
|
"@types/node": "^22.10.5",
|
|
73
71
|
"dazscript-types": "^1.0.1",
|
|
74
72
|
"eslint": "^9.17.0",
|
|
75
73
|
"typescript": "^5.7.3",
|
|
76
|
-
"vitest": "^
|
|
74
|
+
"vitest": "^4.1.8"
|
|
77
75
|
}
|
|
78
76
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
const dialog = vi.hoisted(() => ({ exec: vi.fn(() => true), close: vi.fn() }))
|
|
4
|
+
const restoreObjectName = vi.hoisted(() => vi.fn())
|
|
5
|
+
|
|
6
|
+
vi.mock('./builders/dialog-builder', () => ({
|
|
7
|
+
DialogBuilder: class {
|
|
8
|
+
context = { dialog }
|
|
9
|
+
|
|
10
|
+
build(setup: () => void) {
|
|
11
|
+
setup()
|
|
12
|
+
return dialog
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
restoreObjectName() {
|
|
16
|
+
restoreObjectName()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
import { BasicDialog } from './basic-dialog'
|
|
22
|
+
|
|
23
|
+
class TestDialog extends BasicDialog {
|
|
24
|
+
builds = 0
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
super('Test')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected build(): void {
|
|
31
|
+
this.builds++
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe('BasicDialog.prepare', () => {
|
|
36
|
+
beforeEach(() => vi.clearAllMocks())
|
|
37
|
+
|
|
38
|
+
it('builds and returns the dialog without entering its modal event loop', () => {
|
|
39
|
+
const subject = new TestDialog()
|
|
40
|
+
|
|
41
|
+
const prepared = subject.prepare()
|
|
42
|
+
|
|
43
|
+
expect(prepared).toBe(dialog)
|
|
44
|
+
expect(subject.builds).toBe(1)
|
|
45
|
+
expect(dialog.exec).not.toHaveBeenCalled()
|
|
46
|
+
expect(restoreObjectName).toHaveBeenCalledOnce()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -21,10 +21,14 @@ export abstract class BasicDialog {
|
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
prepare(): DzBasicDialog {
|
|
25
25
|
this.init()
|
|
26
26
|
this.builder.restoreObjectName()
|
|
27
|
-
return this.dialog
|
|
27
|
+
return this.dialog
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
run(): boolean {
|
|
31
|
+
return this.prepare().exec()
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
ok(): boolean {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { Observable } from '@dsf/lib/observable'
|
|
3
|
+
import { TreeNode } from '@dsf/lib/tree-node'
|
|
4
|
+
|
|
5
|
+
const listView = vi.hoisted(() => ({
|
|
6
|
+
items: [] as any[],
|
|
7
|
+
addColumn: vi.fn(),
|
|
8
|
+
clear: vi.fn(function (this: any) { this.items = [] }),
|
|
9
|
+
columnWidth: vi.fn(() => 100),
|
|
10
|
+
setColumnWidth: vi.fn(),
|
|
11
|
+
getItems: vi.fn(function (this: any) { return this.items.slice() }),
|
|
12
|
+
deleteItem: vi.fn(function (this: any, item: any) {
|
|
13
|
+
this.items = this.items.filter((candidate: any) => candidate !== item)
|
|
14
|
+
}),
|
|
15
|
+
setSorting: vi.fn(),
|
|
16
|
+
hide: vi.fn(),
|
|
17
|
+
show: vi.fn(),
|
|
18
|
+
selectedItem: vi.fn(() => null),
|
|
19
|
+
doubleClicked: { scriptConnect: vi.fn() },
|
|
20
|
+
contextMenuRequested: { scriptConnect: vi.fn() }
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
vi.mock('./widget-builder', () => ({
|
|
24
|
+
createWidget: () => ({ build: () => listView })
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
vi.mock('@dsf/helpers/list-view-helper', () => ({
|
|
28
|
+
clearColumns: vi.fn(),
|
|
29
|
+
filter: vi.fn(),
|
|
30
|
+
getDataItem: (item: any) => item?.data,
|
|
31
|
+
setDataItem: (item: any, data: any) => item.data = data
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
class FakeListViewItem {
|
|
35
|
+
data: any
|
|
36
|
+
open = false
|
|
37
|
+
selectable = true
|
|
38
|
+
textByColumn: Record<number, string> = {}
|
|
39
|
+
|
|
40
|
+
constructor(parent: any, public id: number) {
|
|
41
|
+
parent.items.push(this)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setText(column: number, text: string) {
|
|
45
|
+
this.textByColumn[column] = text
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
vi.stubGlobal('DzListView', { All: 0, Extended: 1 })
|
|
50
|
+
vi.stubGlobal('DzListViewItem', FakeListViewItem)
|
|
51
|
+
|
|
52
|
+
import { ListViewBuilder } from './list-view-builder'
|
|
53
|
+
|
|
54
|
+
describe('ListViewBuilder item updates', () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
listView.items = []
|
|
57
|
+
vi.clearAllMocks()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('keeps unchanged rows when data has no explicit id', () => {
|
|
61
|
+
const items = new Observable([
|
|
62
|
+
new TreeNode('Action A', '', { name: 'ActionA' }),
|
|
63
|
+
new TreeNode('Action B', '', { name: 'ActionB' })
|
|
64
|
+
])
|
|
65
|
+
const builder = new ListViewBuilder<any, any>({ dialog: {}, layout: null } as any)
|
|
66
|
+
|
|
67
|
+
builder
|
|
68
|
+
.items(items)
|
|
69
|
+
.columns(['Name'])
|
|
70
|
+
.text(item => [item.name])
|
|
71
|
+
.data(item => item.value)
|
|
72
|
+
.build()
|
|
73
|
+
|
|
74
|
+
items.value = [new TreeNode('Action B', '', { name: 'ActionB' })]
|
|
75
|
+
|
|
76
|
+
expect(listView.items.map(item => item.textByColumn[0])).toEqual(['Action B'])
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
const actionPixmap = vi.hoisted(() => vi.fn())
|
|
4
|
+
|
|
5
|
+
vi.mock('@dsf/common/log', () => ({ debug: vi.fn(), warn: vi.fn() }))
|
|
6
|
+
vi.mock('@dsf/core/global', () => ({
|
|
7
|
+
mainWindow: { getActionMgr: () => ({}) }
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
import { getActionPixmap } from './action-helper'
|
|
11
|
+
|
|
12
|
+
describe('getActionPixmap', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
actionPixmap.mockReset()
|
|
15
|
+
vi.stubGlobal('App', {
|
|
16
|
+
getStyle: () => ({ actionPixmap })
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('rejects a null pixmap even when DAZ reports nonzero height', () => {
|
|
21
|
+
actionPixmap.mockReturnValue({
|
|
22
|
+
height: 16,
|
|
23
|
+
isNull: () => true
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
expect(getActionPixmap('DzTestAction', '')).toBeNull()
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -166,12 +166,12 @@ export const getActionPixmap = (action: string, icon: string, maxSize?: number):
|
|
|
166
166
|
} else {
|
|
167
167
|
pixmap = new Pixmap(image.getFilename())
|
|
168
168
|
}
|
|
169
|
-
return pixmap
|
|
169
|
+
return isEmptyPixmap(pixmap) ? null : pixmap
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
else if (!isCustom) {
|
|
173
173
|
let pixmap = App.getStyle().actionPixmap(action, 0, 0)
|
|
174
|
-
return pixmap
|
|
174
|
+
return isEmptyPixmap(pixmap) ? null : pixmap
|
|
175
175
|
}
|
|
176
176
|
} catch (error) {
|
|
177
177
|
warn(`Error while trying to get icon pixmap: ${error}`)
|
|
@@ -179,4 +179,9 @@ export const getActionPixmap = (action: string, icon: string, maxSize?: number):
|
|
|
179
179
|
return null
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
const isEmptyPixmap = (pixmap: Pixmap | null | undefined): boolean =>
|
|
183
|
+
!pixmap
|
|
184
|
+
|| (typeof (pixmap as any).isNull === 'function' && (pixmap as any).isNull())
|
|
185
|
+
|| pixmap.height === 0
|
|
186
|
+
|
|
182
187
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
const open = vi.hoisted(() => vi.fn(() => false))
|
|
4
|
+
const write = vi.hoisted(() => vi.fn(() => 0))
|
|
5
|
+
const close = vi.hoisted(() => vi.fn())
|
|
6
|
+
|
|
7
|
+
vi.mock('@dsf/common/log', () => ({ error: vi.fn() }))
|
|
8
|
+
|
|
9
|
+
class FakeFile {
|
|
10
|
+
static WriteOnly = 2
|
|
11
|
+
|
|
12
|
+
open = open
|
|
13
|
+
write = write
|
|
14
|
+
close = close
|
|
15
|
+
deleteLater = vi.fn()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class FakeFileInfo {
|
|
19
|
+
absolutePath() {
|
|
20
|
+
return 'C:/temp'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
deleteLater() { }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class FakeDir {
|
|
27
|
+
mkpath() {
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
vi.stubGlobal('DzFile', FakeFile)
|
|
33
|
+
vi.stubGlobal('DzFileInfo', FakeFileInfo)
|
|
34
|
+
vi.stubGlobal('DzDir', FakeDir)
|
|
35
|
+
|
|
36
|
+
import { saveToFile } from './file-helper'
|
|
37
|
+
|
|
38
|
+
describe('saveToFile', () => {
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
vi.clearAllMocks()
|
|
41
|
+
open.mockReturnValue(false)
|
|
42
|
+
write.mockReturnValue(-1)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('fails without writing when the file cannot be opened', () => {
|
|
46
|
+
expect(saveToFile('C:/temp/settings.json', '{}')).toBe(false)
|
|
47
|
+
expect(open).toHaveBeenCalledWith(FakeFile.WriteOnly)
|
|
48
|
+
expect(write).not.toHaveBeenCalled()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('fails when writing reports an error', () => {
|
|
52
|
+
open.mockReturnValue(true)
|
|
53
|
+
|
|
54
|
+
expect(saveToFile('C:/temp/settings.json', '{}')).toBe(false)
|
|
55
|
+
expect(write).toHaveBeenCalledWith('{}')
|
|
56
|
+
expect(close).toHaveBeenCalledOnce()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('fails when writing stores no bytes', () => {
|
|
60
|
+
open.mockReturnValue(true)
|
|
61
|
+
write.mockReturnValue(0)
|
|
62
|
+
|
|
63
|
+
expect(saveToFile('C:/temp/settings.json', '{}')).toBe(false)
|
|
64
|
+
expect(close).toHaveBeenCalledOnce()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('succeeds when the file opens and writes', () => {
|
|
68
|
+
open.mockReturnValue(true)
|
|
69
|
+
write.mockReturnValue(2)
|
|
70
|
+
|
|
71
|
+
expect(saveToFile('C:/temp/settings.json', '{}')).toBe(true)
|
|
72
|
+
expect(close).toHaveBeenCalledOnce()
|
|
73
|
+
})
|
|
74
|
+
})
|
|
@@ -32,22 +32,26 @@ export const readFromFile = <T>(filePath: string, cache: boolean = false): T | n
|
|
|
32
32
|
* @returns
|
|
33
33
|
*/
|
|
34
34
|
export const saveToFile = (filePath: string, content: string): boolean => {
|
|
35
|
+
let fileInfo: DzFileInfo | null = null
|
|
36
|
+
let file: DzFile | null = null
|
|
37
|
+
let opened = false
|
|
35
38
|
try {
|
|
36
39
|
if (!filePath || !content) return false
|
|
37
|
-
|
|
40
|
+
fileInfo = new DzFileInfo(filePath)
|
|
38
41
|
let path = fileInfo.absolutePath()
|
|
39
42
|
var dzDir = new DzDir(path)
|
|
40
43
|
dzDir.mkpath(path)
|
|
41
|
-
|
|
42
|
-
file.open(DzFile.WriteOnly)
|
|
43
|
-
|
|
44
|
-
file.
|
|
45
|
-
fileInfo.deleteLater()
|
|
46
|
-
file.deleteLater()
|
|
47
|
-
return true
|
|
44
|
+
file = new DzFile(`${filePath}`)
|
|
45
|
+
opened = file.open(DzFile.WriteOnly)
|
|
46
|
+
if (!opened) return false
|
|
47
|
+
return file.write(content) > 0
|
|
48
48
|
} catch (error) {
|
|
49
49
|
log.error(`Error while saving file ${filePath}`)
|
|
50
50
|
return false
|
|
51
|
+
} finally {
|
|
52
|
+
if (opened) file?.close()
|
|
53
|
+
fileInfo?.deleteLater()
|
|
54
|
+
file?.deleteLater()
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -105,6 +105,15 @@ describe('probe option resolution', () => {
|
|
|
105
105
|
})
|
|
106
106
|
|
|
107
107
|
describe('integration command resolution', () => {
|
|
108
|
+
it('exposes a no-content smoke integration command', () => {
|
|
109
|
+
const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../package.json'), 'utf8'))
|
|
110
|
+
const script = packageJson.scripts['test:integration:smoke']
|
|
111
|
+
|
|
112
|
+
expect(script).toContain('integration')
|
|
113
|
+
expect(script).toContain('./test/integration/fixtures/framework-smoke.dsa.ts')
|
|
114
|
+
expect(script).not.toContain('--require-content')
|
|
115
|
+
})
|
|
116
|
+
|
|
108
117
|
it('uses node plus npm-cli on Windows so child_process can spawn without a shell', () => {
|
|
109
118
|
const invocation = getNpmInvocation(['--version'], 'win32')
|
|
110
119
|
|
package/webpack.config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const
|
|
2
|
+
const { globSync } = require('glob');
|
|
3
3
|
const webpack = require('webpack');
|
|
4
4
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
|
5
5
|
const { createActionLaunchers } = require('./dist/scripts/launchers');
|
|
@@ -41,8 +41,7 @@ module.exports = (env, argv) => {
|
|
|
41
41
|
const isFileSpecified = env && env.file;
|
|
42
42
|
const entryFiles = isFileSpecified
|
|
43
43
|
? [path.resolve(sourceRoot, `${env.file}.dsa.ts`)]
|
|
44
|
-
:
|
|
45
|
-
.sync(path.join(sourceRoot, '**/*.dsa.ts').replace(/\\/g, '/'))
|
|
44
|
+
: globSync(path.join(sourceRoot, '**/*.dsa.ts').replace(/\\/g, '/'))
|
|
46
45
|
.map((filePath) => path.resolve(filePath));
|
|
47
46
|
|
|
48
47
|
const outputPath =
|