Skip to content

Commit

Permalink
feature: added 'rejectNullish' setting that, when set to 'true', ensu…
Browse files Browse the repository at this point in the history
…res createReport throws when the result of an INS command turns out to be null or undefined.
  • Loading branch information
jjhbw committed Jun 11, 2020
1 parent 336b839 commit 40d0804
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 1 deletion.
Binary file added src/__tests__/fixtures/rejectNullishIMAGE.docx
Binary file not shown.
Binary file added src/__tests__/fixtures/rejectNullishINS.docx
Binary file not shown.
89 changes: 89 additions & 0 deletions src/__tests__/indexNode.test.ts
Expand Up @@ -1023,6 +1023,7 @@ Morbi dignissim consequat ex, non finibus est faucibus sodales. Integer sed just
});

it('131 correctly handles Office 365 .docx files', async () => {
// These files tend to contain a differently named document.xml (like document2.xml)
const template = await fs.promises.readFile(
path.join(__dirname, 'fixtures', 'office365.docx')
);
Expand All @@ -1041,6 +1042,94 @@ Morbi dignissim consequat ex, non finibus est faucibus sodales. Integer sed just
);
expect(result).toMatchSnapshot();
});

describe('rejectNullish setting', () => {
it('INS', async () => {
const template = await fs.promises.readFile(
path.join(__dirname, 'fixtures', 'rejectNullishINS.docx')
);

// When not explicitly set, rejectNullish should be considered 'true' so this case should resolve.
await expect(
createReport({
noSandbox,
template,
data: {
testobj: {}, // accessing a non-existing property will result in `undefined`
test2: 'second value!',
},
})
).resolves.toBeInstanceOf(Uint8Array);

// The same case should throw when we decide NOT to accept nullish values.
await expect(
createReport({
noSandbox,
template,
data: {
testobj: {}, // accessing a non-existing property will result in `undefined`
test2: 'second value!',
},
rejectNullish: true,
})
).rejects.toBeInstanceOf(Error);

// Should be ok when we actually set the value.
await expect(
createReport({
noSandbox,
template,
data: {
testobj: { value: 'the value is now set' },
test2: 'second value!',
},
rejectNullish: true,
})
).resolves.toBeInstanceOf(Uint8Array);
});

it('IMAGE', async () => {
const template = await fs.promises.readFile(
path.join(__dirname, 'fixtures', 'rejectNullishIMAGE.docx')
);
await expect(
createReport({
noSandbox,
template,
data: {},
additionalJsContext: {
qr: () => undefined,
},
})
).resolves.toBeInstanceOf(Uint8Array);
await expect(
createReport({
noSandbox,
template,
data: {},
rejectNullish: true,
additionalJsContext: {
qr: () => undefined,
},
})
).rejects.toBeInstanceOf(Error);
await expect(
createReport({
noSandbox,
template,
data: {},
rejectNullish: true,
additionalJsContext: {
qr: async (contents: string) => {
const dataUrl = await QR.toDataURL(contents, { width: 500 });
const data = dataUrl.slice('data:image/gif;base64,'.length);
return { width: 6, height: 6, data, extension: '.gif' };
},
},
})
).resolves.toBeInstanceOf(Uint8Array);
});
});
});
});
});
2 changes: 2 additions & 0 deletions src/main.ts
Expand Up @@ -88,6 +88,8 @@ async function createReport(
runJs: options.runJs,
additionalJsContext: options.additionalJsContext || {},
failFast: options.failFast == null ? true : options.failFast,
rejectNullish:
options.rejectNullish == null ? false : options.rejectNullish,
};
const xmlOptions = { literalXmlDelimiter };

Expand Down
24 changes: 23 additions & 1 deletion src/processTemplate.ts
Expand Up @@ -476,7 +476,14 @@ const processCmd = async (
} else if (cmdName === 'INS') {
if (!isLoopExploring(ctx)) {
const result = await runUserJsAndGetRaw(data, cmdRest, ctx);
if (result == null) return '';
if (result == null) {
if (ctx.options.rejectNullish) {
throw new Error(
`Result of '${cmdRest}' is null or undefined and rejectNullish is set`
);
}
return '';
}

// If the `processLineBreaks` flag is set,
// newlines are replaced with a `w:br` tag (protected by
Expand Down Expand Up @@ -504,6 +511,11 @@ const processCmd = async (
cmdRest,
ctx
);
if (ctx.options.rejectNullish && img == null) {
throw new Error(
`Result of '${cmdRest}' is null or undefined and rejectNullish is set`
);
}
if (img != null) await processImage(ctx, img);
}

Expand All @@ -515,6 +527,11 @@ const processCmd = async (
cmdRest,
ctx
);
if (ctx.options.rejectNullish && pars == null) {
throw new Error(
`Result of '${cmdRest}' is null or undefined and rejectNullish is set`
);
}
if (pars != null) await processLink(ctx, pars);
}

Expand All @@ -526,6 +543,11 @@ const processCmd = async (
cmdRest,
ctx
);
if (ctx.options.rejectNullish && html == null) {
throw new Error(
`Result of '${cmdRest}' is null or undefined and rejectNullish is set`
);
}
if (html != null) await processHtml(ctx, html);
}

Expand Down
7 changes: 7 additions & 0 deletions src/types.ts
Expand Up @@ -95,6 +95,12 @@ export type UserOptions = {
* Whether to fail on the first error encountered in the template. Defaults to true. Can be used to collect all errors in a template (e.g. misspelled commands) before failing.
*/
failFast?: boolean;

/**
* When set to `true`, this setting ensures createReport throws an error when the result of an INS, HTML, IMAGE, or LINK command turns out to be null or undefined,
* as this usually indicates a mistake in the template or the invoking code (defaults to `false`).
*/
rejectNullish?: boolean;
};

export type CreateReportOptions = {
Expand All @@ -105,6 +111,7 @@ export type CreateReportOptions = {
runJs?: RunJSFunc;
additionalJsContext: Object;
failFast: boolean;
rejectNullish: boolean;
};

export type Context = {
Expand Down

0 comments on commit 40d0804

Please sign in to comment.