Skip to content

Commit 81fe514

Browse files
committed
Remove xml when there is an error converting form
1 parent e12b019 commit 81fe514

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

src/lib/convert-forms/index.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const argsFormFilter = require('../args-form-filter');
22
const exec = require('../exec-promise');
33
const fs = require('../sync-fs');
4+
const nodeFs = require('node:fs');
45
const {
56
getFormDir,
67
escapeWhitespacesInPath,
@@ -46,9 +47,16 @@ const execute = async (projectDir, subDirectory, options = {}) => {
4647

4748
info('Converting form', sourcePath, '…');
4849

49-
await xls2xform(escapeWhitespacesInPath(sourcePath), escapeWhitespacesInPath(targetPath), xls);
50-
const hiddenFields = await getHiddenFields(`${fs.withoutExtension(sourcePath)}.properties.json`);
51-
fixXml(targetPath, hiddenFields, options.transformer, options.enketo);
50+
try {
51+
await xls2xform(escapeWhitespacesInPath(sourcePath), escapeWhitespacesInPath(targetPath), xls);
52+
const hiddenFields = await getHiddenFields(`${fs.withoutExtension(sourcePath)}.properties.json`);
53+
fixXml(targetPath, hiddenFields, options.transformer, options.enketo);
54+
} catch (e) {
55+
// Remove xml file to avoid possibly leaving it in an invalid state
56+
nodeFs.rmSync(targetPath, { force: true });
57+
throw e;
58+
}
59+
5260
trace('Converted form', sourcePath);
5361
}
5462
};

src/lib/upload-forms.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const updateFromPropertiesFile = (baseFileName, doc, path, supported_properties)
121121
Object
122122
.keys(properties)
123123
.forEach(key => {
124-
if (typeof properties[key] === 'undefined') {
124+
if (properties[key] === undefined) {
125125
return;
126126
}
127127
if (supported_properties.includes(key)) {

test/lib/convert-forms/index.spec.js

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const sinon = require('sinon');
33
const rewire = require('rewire');
44

55
const fs = require('../../../src/lib/sync-fs');
6+
const nodeFs = require('node:fs');
67
const path = require('path');
78
const log = require('../../../src/lib/log');
89
const { LEVEL_NONE } = log;
@@ -14,14 +15,15 @@ describe('convert-forms', () => {
1415
let mockExec;
1516
beforeEach(() => {
1617
mockExec = sinon.stub();
17-
sinon.stub(log, 'warn');
18+
convertForms.__set__('warn', sinon.stub(log, 'warn'));
1819
convertForms.__set__('exec', mockExec);
1920
convertForms.__set__('fixXml', sinon.stub());
2021
convertForms.__set__('getHiddenFields', sinon.stub());
2122

2223
sinon.stub(fs, 'readdir').returns(['a.xml', 'b.xlsx', 'c.xlsx']);
2324
sinon.stub(fs, 'exists').returns(true);
2425
sinon.stub(fs, 'readJson').returns({});
26+
sinon.stub(nodeFs, 'rmSync');
2527
});
2628
afterEach(sinon.restore);
2729

@@ -38,28 +40,31 @@ describe('convert-forms', () => {
3840
await expect(convertForms.execute('./path', 'app')).to.be.rejectedWith(
3941
`There was a problem executing xls2xform. Make sure you have Python 3.10+ installed.\n${message}`
4042
);
43+
expect(nodeFs.rmSync).to.have.been.calledOnceWithExactly('./path/forms/app/b.xml', { force: true });
4144
});
4245
});
4346

44-
it('throws error with empty string', () => async () => {
47+
it('throws error with empty string', async () => {
4548
mockExec.returns(Promise.reject(''));
4649

4750
await expect(convertForms.execute('./path', 'app')).to.be.rejectedWith(
4851
`There was a problem executing xls2xform. Make sure you have Python 3.10+ installed.\n`
4952
);
53+
expect(nodeFs.rmSync).to.have.been.calledOnceWithExactly('./path/forms/app/b.xml', { force: true });
5054
});
5155

52-
it('throws error with empty object', () => async () => {
56+
it('throws error with empty object', async () => {
5357
mockExec.returns(Promise.reject({}));
5458

5559
await expect(convertForms.execute('./path', 'app')).to.be.rejectedWith(
5660
`There was a problem executing xls2xform. Make sure you have Python 3.10+ installed.\n{}`
5761
);
62+
expect(nodeFs.rmSync).to.have.been.calledOnceWithExactly('./path/forms/app/b.xml', { force: true });
5863
});
5964
});
6065

6166
describe('pyxform execution completes', () => {
62-
it('succeeds when OK status code', () => async () => {
67+
it('succeeds when OK status code', async () => {
6368
mockExec.returns(Promise.resolve(JSON.stringify({ code: 100 })));
6469

6570
await convertForms.execute('./path', 'app');
@@ -68,9 +73,10 @@ describe('convert-forms', () => {
6873
[[XLS2XFORM, '--skip_validate', '--json', './path/forms/app/b.xlsx', './path/forms/app/b.xml'], LEVEL_NONE],
6974
[[XLS2XFORM, '--skip_validate', '--json', './path/forms/app/c.xlsx', './path/forms/app/c.xml'], LEVEL_NONE]
7075
]);
76+
expect(nodeFs.rmSync).to.not.have.been.called;
7177
});
7278

73-
it('prints warnings before succeeding', () => async () => {
79+
it('prints warnings before succeeding', async () => {
7480
const warnings = ['Warning 1', 'Warning 2'];
7581
mockExec.returns(Promise.resolve(JSON.stringify({ code: 101, warnings })));
7682

@@ -86,28 +92,31 @@ describe('convert-forms', () => {
8692
['Converted c.xlsx with warnings:'],
8793
...warnings.map(w => [w])
8894
]);
95+
expect(nodeFs.rmSync).to.not.have.been.called;
8996
});
9097

91-
it('throws error when xls2xform reports an error', () => async () => {
98+
it('throws error when xls2xform reports an error', async () => {
9299
const message = 'There has been a problem trying to replace ${doesNOtExist} with ' +
93100
'the XPath to the survey element named \'doesNOtExist\'. There is no survey element with this name.';
94101
mockExec.returns(Promise.resolve(JSON.stringify({ code: 999, message })));
95102

96103
await expect(convertForms.execute('./path', 'app')).to.be.rejectedWith(
97104
`Could not convert b.xlsx: ${message}`
98105
);
106+
expect(nodeFs.rmSync).to.have.been.calledOnceWithExactly('./path/forms/app/b.xml', { force: true });
99107
});
100108

101-
it('throws custom error when xls2xform reports an empty group', () => async () => {
109+
it('throws custom error when xls2xform reports an empty group', async () => {
102110
const message = '\'NoneType\' object is not iterable';
103111
mockExec.returns(Promise.resolve(JSON.stringify({ message })));
104112

105113
await expect(convertForms.execute('./path', 'app')).to.be.rejectedWith(
106114
'Could not convert b.xlsx: Check the form for an empty group or repeat.'
107115
);
116+
expect(nodeFs.rmSync).to.have.been.calledOnceWithExactly('./path/forms/app/b.xml', { force: true });
108117
});
109118

110-
it('warns of any additional messages included in log', () => async () => {
119+
it('warns of any additional messages included in log', async () => {
111120
const msg0 = 'UserWarning: Data Validation extension is not supported and will be removed';
112121
const msg1 = 'warn(msg)';
113122
mockExec.returns(Promise.resolve(`
@@ -123,33 +132,34 @@ describe('convert-forms', () => {
123132
[[XLS2XFORM, '--skip_validate', '--json', './path/forms/app/c.xlsx', './path/forms/app/c.xml'], LEVEL_NONE]
124133
]);
125134
expect(log.warn.args).to.deep.equal([[msg0], [msg1], [msg0], [msg1]]);
135+
expect(nodeFs.rmSync).to.not.have.been.called;
126136
});
127137
});
128138

129139
describe('filtering', () => {
130140
beforeEach(() => mockExec.resolves(JSON.stringify({ code: 100 })));
131141

132-
it('filter matches one form only', () => async () => {
142+
it('filter matches one form only', async () => {
133143
await convertForms.execute('./path', 'app', { forms: ['c'] });
134144
expect(mockExec).calledOnceWithExactly(
135145
[XLS2XFORM, '--skip_validate', '--json', './path/forms/app/c.xlsx', './path/forms/app/c.xml'], LEVEL_NONE
136146
);
137147
});
138148

139-
it('filter matches no forms', () => async () => {
149+
it('filter matches no forms', async () => {
140150
await convertForms.execute('./path', 'app', { forms: ['z'] });
141151
expect(mockExec).to.not.have.been.called;
142152
});
143153

144-
it('--debug does not filter', () => async () => {
154+
it('--debug does not filter', async () => {
145155
await convertForms.execute('./path', 'app', { forms: ['--debug'] });
146156
expect(mockExec.args).to.deep.equal([
147157
[[XLS2XFORM, '--skip_validate', '--json', './path/forms/app/b.xlsx', './path/forms/app/b.xml'], LEVEL_NONE],
148158
[[XLS2XFORM, '--skip_validate', '--json', './path/forms/app/c.xlsx', './path/forms/app/c.xml'], LEVEL_NONE]
149159
]);
150160
});
151161

152-
it('escape whitespaces in path and convert forms', () => async () => {
162+
it('escape whitespaces in path and convert forms', async () => {
153163
await convertForms.execute('./path with space', 'app');
154164
expect(mockExec.args).to.deep.equal([
155165
[[

0 commit comments

Comments
 (0)