Skip to content

Commit

Permalink
#6 #3 First integration test, and assertion checker,
Browse files Browse the repository at this point in the history
#7 Add some documentation
  • Loading branch information
Pawel Siemienik committed Mar 23, 2020
1 parent 5003c1a commit b1fe230
Show file tree
Hide file tree
Showing 20 changed files with 153 additions and 67 deletions.
6 changes: 1 addition & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,4 @@
/.idea
/coverage/
/.nyc_output/
/.idea/


/src/example.ts
*.xlsx
/.idea/
80 changes: 47 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

# WIP current version of lib is under developing

# Introduction

This library makes generating xlsx files (Excel) easly.

It consumes template which is common Excel file, then add yours data (called ViewModel). Blend it and done, as result you'll get pretty Excel.

# Getting Started:

1. install package
Expand All @@ -11,50 +17,58 @@
npm i xlsx-renderer --save
```

`npm install`
2. TODO add some example here (https://github.com/Siemienik/xlsx-renderer/issues/6)

## In Cell Commands:

1. `## varToDisplayInThisCell`
2. `#! FINISH` - finish processing current worksheet
2. `#! FINISH booleanVar` - if true it finishes processing current template worksheet, if false it add again this worksheet to output
3. `#! END_ROW`
4. `#! DELETE varName`
5. `#! HYPERLINK labelVar urlVar`
6. `#! WS_NAME nameVar` set worksheet name
7. `#! FOR_EACH item collection` (to write item property `## item.property`),
8. `#! CONTINUE item` item is set to the next collection item.
9. `#! END_LOOP item`
10. `#! AVERAGE item` write average formula of all items from previous for-each, it has to be placed after the for-each was finished.
11. `#! SUM item` similar to average
12. `#! DUMP_COLS arrayVar` write to next columns all array items (1 item = 1 column)


## Sample code: (possible to be outdated yet - todo)
## Sample code:

```javascript
import Renderer from './xls-renderer/Renderer'
import {Workbook} from 'exceljs'

//*
import DebugCellTemplatePool from "./xls-renderer-debug/CellTemplateDebugPool";
const renderer = new Renderer(new DebugCellTemplatePool());
/*/
import CellTemplatePool from "./xls-renderer/CellTemplatePool";
const renderer = new Renderer(new CellTemplatePool());
//*/


const viewModel = new MyAwesomeReportVm(); //or something else

(async () => {
const result = await renderer.render(async () => {
const template = new Workbook();
return await template.xlsx.readFile('./my-awesome-raport-template.xlsx');
}, viewModel);
const result = await renderer.renderFromFile('./my-awesome-raport-template.xlsx', viewModel);

await result.xlsx.writeFile('./my-awesome-raport.xlsx');
})();
```


# Documentation:

## Cells:

| Category | Name | Order | match rule | Description | More info |
|----------|-----:|-------|--------|-------------|:---------|
| - | [BaseCell](./src/cell/BaseCell.ts) | n/o | n/o | All Cell\`s definition classes extend it. | **abstract** |
| Content | [NormalCell](./src/cell/NormalCell.ts) | 1 | not started by `##` or `#!` | This one copy all styles, width, properties and value form template. | **default** |
| Content | [VariableCell](./src/cell/VariableCell.ts) | 3 | `## pathToVariable ` | Write variable from `ViewModel`. <br/> Paths to object's property or array item are allowed. | **Examples:** <br/> `simplePath` <br/> `someObject.property` <br/> `array.0.field` <br/> `items.1.path.to.object.prop`|
| Content | **TODO: describe it!** [HyperlinkCell](./src/cell/HyperlinkCell.ts) | | | | |
| Content | **TODO: describe it!** [FormulaCell](./src/cell/FormulaCell.ts) | | | | |
| Navigation | **TODO: describe it!** [EndRowCell](./src/cell/EndRowCell.ts) | | | | |
| Worksheet<br/>Navigation<br/>Loop | [FinishCell](./src/cell/FinishCell.ts) | 7 | `#! FINISH conditionPath` | Finish rendering for current worksheet and: <br/> 1) go to next worksheet if `conditionPath===true`<br/> 2) repeat this template worksheet again (`conditionPath === false`) - looping through worksheets <br/> 3) finished whole rendering when this worksheet is the last one. | **Examples:**<br/> `#! FINISHED ` or `#! FINISHED itemFromLoop.__iterated` |
| Worksheet | **TODO: describe it!** [WsNameCell](./src/cell/WsNameCell.ts) | | | | |
| View Model | **TODO: describe it!** [DeleteCell](./src/cell/DeleteCell.ts) | | | | |
| Loop | **TODO: describe it!** [DumpColsCell](./src/cell/DumpColsCell.ts) | | | | |
| Loop | **TODO: describe it!** [ForEachCell](./src/cell/ForEachCell.ts) | | | | |
| Loop | **TODO: describe it!** [ContinueCell](./src/cell/ContinueCell.ts) | | | | |
| Loop | **TODO: describe it!** [EndLoopCell](./src/cell/EndLoopCell.ts) | | | | |
| Aggregation | **TODO: describe it!** [AverageCell](./src/cell/AverageCell.ts) | | | | |
| Aggregation| **TODO: describe it!** [SumCell](./src/cell/SumCell.ts) | | | | |


## Commands [PREVIOUS VERSION]:

1. `#! END_ROW`
4. `#! DELETE varName`
5. `#! HYPERLINK labelVar urlVar`
6. `#! WS_NAME nameVar` set worksheet name
7. `#! FOR_EACH item collection` (to write item property `## item.property`),
8. `#! CONTINUE item` item is set to the next collection item.
9. `#! END_LOOP item`
10. `#! AVERAGE item` write average formula of all items from previous for-each, it has to be placed after the for-each was finished.
11. `#! SUM item` similar to average
12. `#! DUMP_COLS arrayVar` write to next columns all array items (1 item = 1 column)


[LICENSE](LICENSE)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"postversion": "git push && git push --tags"
},
"devDependencies": {
"@types/node": "^12.12.6",
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.6",
"chai": "^4.2.0",
Expand Down
9 changes: 9 additions & 0 deletions src/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ export class Renderer {

return output;
}

public async renderFromFile(templatePath: string, viewModel: any): Promise<Workbook> {
const result = await this.render(async () => {
const template = new Workbook();
return await template.xlsx.readFile(templatePath);
}, viewModel);

return await result;
}
}
4 changes: 2 additions & 2 deletions src/cell/BaseCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { Scope } from '../Scope';

export declare type CellType = typeof BaseCell;

export class BaseCell {
export /* abstract */ class BaseCell { // can't be abstract :(
/**
* check if this commend can parse `value`
*/
public static match(cell: Cell): boolean {
return false;
}

public BaseCell() {
public BaseCell() { // can't be marked by abstract keyword, so it throw type error.
throw new TypeError(`Cannot construct ${BaseCell.name} instances directly. It's abstract.`);
}

Expand Down
27 changes: 0 additions & 27 deletions src/example.ts

This file was deleted.

92 changes: 92 additions & 0 deletions tests/integration/Renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {Renderer} from "../../src/Renderer";
import * as fs from "fs";
import * as path from "path";
import {Workbook} from "exceljs";
import * as chai from 'chai'


function assertCells(expected: Workbook, result: Workbook, factor: number = 10) {
chai.expect(expected.worksheets.length).eql(result.worksheets.length);
chai.expect(expected.worksheets.map(x => x.name)).eql(result.worksheets.map(x => x.name));

for (let wi = 0; wi < expected.worksheets.length; wi++) {
const ws = {e: expected.worksheets[wi], r: result.worksheets[wi]};
for (let i = 0; i < factor * factor; i++) {
const r = Math.floor(i / factor) + 1;
const c = i % factor + 1;
const cell = {
e: ws.e.getCell(r, c),
r: ws.r.getCell(r, c)
};


if (r === 1) {
chai.expect(ws.e.getColumn(c).width).eql(ws.r.getColumn(c).width);
}
if (c === 1) {
chai.expect(ws.e.getRow(r).height).eql(ws.r.getRow(r).height);
}

chai.expect(cell.e.style).eql(cell.r.style);
chai.expect(cell.e.text).eql(cell.r.text);
chai.expect(cell.e.value).eql(cell.r.value);
}
}
}

describe('INTEGRATION:: Test xlsx renderer ', function () {

describe('Checking if assertCells works ok.', function () {
it('Same - should pass ok', async function () {
const expected = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'main.xlsx'));
const correct = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'correct.xlsx'));

assertCells(expected, correct, 20);
});

it('Different - attempt to broke assertions', async function () {
const expected = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'main.xlsx'));
const failedWorksheetAmount = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-ws-amount.xlsx'));
const failedWorksheetNames = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-ws-names.xlsx'));
const failedWidth = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-width.xlsx'));
const failedHeight = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-height.xlsx'));
const failedStyle = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-style.xlsx'));
const failedText = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-text.xlsx'));
const failedValue = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-value.xlsx'));
const failedTable = await new Workbook().xlsx.readFile(path.join(__dirname, 'data', 'assertCells', 'f-table.xlsx'));

chai.expect(() => assertCells(expected, failedWorksheetAmount, 20)).throw("expected 2 to deeply equal 3");
chai.expect(() => assertCells(expected, failedWorksheetNames, 20)).throw('expected [ \'Sheet1\', \'Sheet2\' ] to deeply equal [ \'Sheet1\', \'Sheet3\' ]');
chai.expect(() => assertCells(expected, failedWidth, 20)).throw("expected 13 to deeply equal 7.90625");
chai.expect(() => assertCells(expected, failedHeight, 20)).throw("expected 15 to deeply equal 34.5");
chai.expect(() => assertCells(expected, failedStyle, 20)).throw("expected { Object (font, border, ...) } to deeply equal { Object (font, border, ...) }");
chai.expect(() => assertCells(expected, failedText, 20)).throw('expected \'sadasd\' to deeply equal \'sadas\'');
chai.expect(() => assertCells(expected, failedValue, 20)).throw('expected { Object (formula, result) } to deeply equal \'asdasda\'');
chai.expect(() => assertCells(expected, failedTable, 20)).throw('expected { Object (font, border, ...) } to deeply equal { Object (font, border, ...) }');
});
});
describe('Load examples, render and compare with expected result', function () {
const dataPath = path.normalize(path.join(__dirname, 'data/'));
const sets = fs.readdirSync(path.normalize(dataPath), {withFileTypes: true})
.filter(i => i.isDirectory())
.filter(d => /^Renderer[0-9]*-/.test(d.name));


const renderer = new Renderer();
sets.forEach(s => {
it(`Test for ${s.name}`, async function () {
const result = await renderer.renderFromFile(
path.join(dataPath, s.name, "template.xlsx"),
require(path.join(dataPath, s.name, 'viewModel.json'))
);

const expected = await new Workbook().xlsx.readFile(path.join(dataPath, s.name, "expected.xlsx"));

assertCells(expected, result);
});


})

});
});
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Binary file not shown.
Binary file not shown.
Binary file added tests/integration/data/assertCells/f-style.xlsx
Binary file not shown.
Binary file not shown.
Binary file added tests/integration/data/assertCells/f-text.xlsx
Binary file not shown.
Binary file added tests/integration/data/assertCells/f-value.xlsx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/integration/data/assertCells/main.xlsx
Binary file not shown.

0 comments on commit b1fe230

Please sign in to comment.