Merge pull request #10 from felipeochoa/numbering

Numbering
This commit is contained in:
Dolan
2017-03-08 19:30:21 +00:00
committed by GitHub
14 changed files with 384 additions and 119 deletions

103
docs/numbering.md Normal file
View File

@ -0,0 +1,103 @@
# Bullets and numbering
DOCX is quite flexible in its bullets and numbering system, allowing
the user great freedom in how bullets and numbers are to be styled and
displayed. E.g., numbers can be shown using Arabic numerals, roman
numerals, or even ordinal words ("one", "two", "three", ...). The
format also supports re-using bullets/numbering styles throughout the
document, so that different lists using the same style need not
redefine them.
Because of this flexibility, bullets and numbering in DOCX involves a
couple of moving pieces:
1. Document-level bullets/numbering definitions (abstract)
2. Document-level bullets/numbering definitions (concrete)
3. Paragraph-level bullets/numbering selection
## Document-level bullets/numbering definitions (abstract)
Every document contains a set of abstract bullets/numbering
definitions which define the formatting and layout of paragraphs using
those bullets/numbering. An abstract numbering system defines how
bullets/numbers are to be shown for lists, including any sublists that
may be used. Thus each abstract definition includes a series of
*levels* which form a sequence starting at 0 indicating the top-level
list look and increasing from there to descibe the sublists, then
sub-sublists, etc. Each level includes the following properties:
- **level**: This its 0-based index in the defintion stack
- **numberFormat**: This indicates how the bullet or number should be
generated. Options include `bullet` (meaning don't count), `decimal`
(arabic numerals), `upperRoman`, `lowerRoman`, `hex`, and many
more.
- **levelText**: This is a format string using the output of the
`numberFormat` function and generating a string to insert before
every item in the list. You may use `%1`, `%2`, ... to reference the
numbers from each numbering level before this one. Thus a level
text of `%d)` with a number format of `lowerLetter` would result in
the sequence "a)", "b)", ...
- and a few others, which you can see in the OXML spec section 17.9.6
## Document-level bullets/numbering defintions (concrete)
Concrete definitions are sort of like concrete subclasses of the
abstract defintions. They indicate their parent and are allowed to
override certain level definitions. Thus two lists that differ only in
how sub-sub-lists are to be displayed can share the same abstract
numbering definition and have slightly different concrete definitions.
## Paragraph-level bullets/numbering selection
In order to use a bullets/numbering definition (which must be
concrete), paragraphs need to select it, similar to applying a CSS
class to an element, using both the concrete numbering definition ID
and the level number that the paragraph should be at. Additionally, MS
Word and LibreOffice typically apply a "ListParagraph" style to
paragraphs that are being numbered.
## Using bullets/numbering in `docx`
`docx` includes a pre-defined bullet style which you can add to your
paragraphs using `para.bullets()`. If you require different bullet
styles or numbering of any kind, you'll have to use the
`docx.Numbering` class.
First you need to create a new numbering container class and use it to
create your abstract numbering style, define your levels, and creat
your concreate numbering style:
```js
const numbering = new Numbering();
const abstractNum = numbering.createAbstractNumbering();
abstractNum.createLevel(0, "upperRoman", "%1", "start")
.addParagraphProperty(new Indent(720, 260));
abstractNum.createLevel(1, "decimal", "%2.", "start")
.addParagraphProperty(new Indent(1440, 980));
abstractNum.createLevel(2, "lowerLetter", "%3)", "start")
.addParagraphProperty(new Indent(2160, 1700));
const concrete = numbering.createConcreteNumbering(numberedAbstract);
```
You can then apply your concrete style to paragraphs using their
`#setNumbering` method:
```js
topLevelP.setNumbering(concrete, 0);
subP.setNumbering(concrete, 1);
subSubP.setNumbering(concrete, 2);
```
Finally, you need to let your exporter know about your numbering
styles when you're ready to render the document:
```js
const packer = new Packer(doc, undefined, undefined, numbering);
packer.pack(myOutput);
```

View File

@ -6,6 +6,7 @@ import {ParagraphProperties} from "./properties";
import {MaxRightTabStop, LeftTabStop} from "./tab-stop";
import {Style} from "./style";
import {NumberProperties} from "./unordered-list";
import { Num } from "../../numbering/num";
class Alignment extends XmlComponent {
@ -106,7 +107,13 @@ export class Paragraph extends XmlComponent {
bullet(): Paragraph {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties());
this.properties.push(new NumberProperties(1, 0));
return this;
}
public setNumbering(numbering: Num, indentLevel: number): Paragraph {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties(numbering.id, indentLevel));
return this;
}
}

View File

@ -3,10 +3,10 @@ import {Style} from "./style";
export class NumberProperties extends XmlComponent {
constructor() {
constructor(numberId: number, indentLevel: number) {
super("w:numPr");
this.root.push(new IndentLevel(0));
this.root.push(new NumberId(1));
this.root.push(new IndentLevel(indentLevel));
this.root.push(new NumberId(numberId));
}
}

View File

@ -1,2 +1,3 @@
export * from "./docx";
export * from "./export";
export { Numbering } from './numbering';

View File

@ -1,42 +1,50 @@
import {XmlComponent} from "../docx/xml-components";
import {XmlAttributeComponent} from "../docx/xml-components";
import * as _ from "lodash";
import { XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
import { Level } from "./level";
import { MultiLevelType } from "./multi-level-type";
import * as _ from "lodash";
interface AbstractNumberingAttributesProperties {
interface IAbstractNumberingAttributesProperties {
abstractNumId?: number;
restartNumberingAfterBreak?: number;
}
class AbstractNumberingAttributes extends XmlAttributeComponent {
constructor(properties: AbstractNumberingAttributesProperties) {
constructor(properties: IAbstractNumberingAttributesProperties) {
super({
abstractNumId: "w:abstractNumId",
restartNumberingAfterBreak: "w15:restartNumberingAfterBreak"
restartNumberingAfterBreak: "w15:restartNumberingAfterBreak",
}, properties);
}
}
export class AbstractNumbering extends XmlComponent {
public id: number;
constructor(id: number) {
super("w:abstractNum");
this.root.push(new AbstractNumberingAttributes({
abstractNumId: id,
restartNumberingAfterBreak: 0
restartNumberingAfterBreak: 0,
}));
this.root.push(new MultiLevelType("hybridMultilevel"));
this.id = id;
}
addLevel(level: Level): void {
public addLevel(level: Level): void {
this.root.push(level);
}
clearVariables() {
_.forEach(this.root, element => {
public createLevel(num: number, format: string, text: string, align: string="start") {
const level = new Level(num, format, text, align);
this.addLevel(level);
return level;
}
public clearVariables(): void {
_.forEach(this.root, (element) => {
element.clearVariables();
});
delete this.id;
}
}

View File

@ -1,4 +1,4 @@
import {XmlComponent, XmlAttributeComponent} from "../docx/xml-components";
import {XmlAttributeComponent, XmlComponent} from "../docx/xml-components";
interface IndentAttributesProperties {
left: number;
@ -10,7 +10,7 @@ class IndentAttributes extends XmlAttributeComponent {
constructor(properties: IndentAttributesProperties) {
super({
left: "w:left",
hanging: "w:hanging"
hanging: "w:hanging",
}, properties);
}
}
@ -21,7 +21,7 @@ export class Indent extends XmlComponent {
super("w:ind");
this.root.push(new IndentAttributes({
left: left,
hanging: hanging
hanging: hanging,
}));
}
}

View File

@ -1,13 +1,14 @@
import {MultiPropertyXmlComponent} from "../docx/xml-components";
import {DocumentAttributes} from "../docx/document/document-attributes";
import {AbstractNumbering} from "./abstract-numbering";
import {Level} from "./level";
import {Indent} from "./indent";
import {RunFonts} from "./run-fonts";
import {Num} from "./num";
import * as _ from "lodash";
import { DocumentAttributes } from "../docx/document/document-attributes";
import { MultiPropertyXmlComponent } from "../docx/xml-components";
import { AbstractNumbering } from "./abstract-numbering";
import { Indent } from "./indent";
import { Level } from "./level";
import { Num } from "./num";
import { RunFonts } from "./run-fonts";
export class Numbering extends MultiPropertyXmlComponent {
private nextId: number;
constructor() {
super("w:numbering");
@ -28,65 +29,69 @@ export class Numbering extends MultiPropertyXmlComponent {
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
Ignorable: "w14 w15 wp14"
Ignorable: "w14 w15 wp14",
}));
let abstractNumbering = new AbstractNumbering(0);
this.nextId = 0;
let level0 = new Level(0, "bullet", "•", "left");
level0.addParagraphProperty(new Indent(720, 360));
level0.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.addLevel(level0);
const abstractNumbering = this.createAbstractNumbering();
let level1 = new Level(1, "bullet", "o", "left");
level1.addParagraphProperty(new Indent(1440, 360));
level1.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.addLevel(level1);
abstractNumbering.createLevel(0, "bullet", "", "left")
.addParagraphProperty(new Indent(720, 360))
.addRunProperty(new RunFonts("Symbol", "default"));
let level2 = new Level(2, "bullet", "", "left");
level2.addParagraphProperty(new Indent(2160, 360));
level2.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.addLevel(level2);
abstractNumbering.createLevel(1, "bullet", "o", "left")
.addParagraphProperty(new Indent(1440, 360))
.addRunProperty(new RunFonts("Courier New", "default"));
let level3 = new Level(3, "bullet", "•", "left");
level3.addParagraphProperty(new Indent(2880, 360));
level3.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.addLevel(level3);
abstractNumbering.createLevel(2, "bullet", "•", "left")
.addParagraphProperty(new Indent(2160, 360))
.addRunProperty(new RunFonts("Wingdings", "default"));
let level4 = new Level(4, "bullet", "o", "left");
level4.addParagraphProperty(new Indent(3600, 360));
level4.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.addLevel(level4);
abstractNumbering.createLevel(3, "bullet", "", "left")
.addParagraphProperty(new Indent(2880, 360))
.addRunProperty(new RunFonts("Symbol", "default"));
let level5 = new Level(5, "bullet", "", "left");
level5.addParagraphProperty(new Indent(4320, 360));
level5.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.addLevel(level5);
abstractNumbering.createLevel(4, "bullet", "o", "left")
.addParagraphProperty(new Indent(3600, 360))
.addRunProperty(new RunFonts("Courier New", "default"));
let level6 = new Level(6, "bullet", "•", "left");
level6.addParagraphProperty(new Indent(5040, 360));
level6.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.addLevel(level6);
abstractNumbering.createLevel(5, "bullet", "•", "left")
.addParagraphProperty(new Indent(4320, 360))
.addRunProperty(new RunFonts("Wingdings", "default"));
let level7 = new Level(4, "bullet", "o", "left");
level7.addParagraphProperty(new Indent(5760, 360));
level7.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.addLevel(level7);
abstractNumbering.createLevel(6, "bullet", "", "left")
.addParagraphProperty(new Indent(5040, 360))
.addRunProperty(new RunFonts("Symbol", "default"));
let level8 = new Level(5, "bullet", "", "left");
level8.addParagraphProperty(new Indent(6480, 360));
level8.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.addLevel(level8);
abstractNumbering.createLevel(7, "bullet", "o", "left")
.addParagraphProperty(new Indent(5760, 360))
.addRunProperty(new RunFonts("Courier New", "default"));
this.root.push(abstractNumbering);
this.root.push(new Num(1, 0));
abstractNumbering.createLevel(8, "bullet", "•", "left")
.addParagraphProperty(new Indent(6480, 360))
.addRunProperty(new RunFonts("Wingdings", "default"));
this.createConcreteNumbering(abstractNumbering);
}
clearVariables() {
public createAbstractNumbering(): AbstractNumbering {
const num = new AbstractNumbering(this.nextId++);
this.root.push(num);
return num;
}
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
const num = new Num(this.nextId++, abstractNumbering.id);
this.root.push(num);
return num;
}
public clearVariables(): void {
super.clearVariables();
_.forEach(this.root, element => {
console.log(element);
_.forEach(this.root, (element) => {
element.clearVariables();
});
delete this.nextId;
}
}

View File

@ -1,19 +1,18 @@
import {XmlComponent, Attributes, MultiPropertyXmlComponent} from "../docx/xml-components";
import {XmlAttributeComponent} from "../docx/xml-components";
import {RunProperties} from "../docx/run/properties";
import { ParagraphProperties } from "../docx/paragraph/properties";
import { RunProperties } from "../docx/run/properties";
import { Attributes, MultiPropertyXmlComponent, XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
interface LevelAttributesProperties {
interface ILevelAttributesProperties {
ilvl?: number;
tentative?: number;
}
class LevelAttributes extends XmlAttributeComponent {
constructor(properties: LevelAttributesProperties) {
constructor(properties: ILevelAttributesProperties) {
super({
ilvl: "w:ilvl",
tentative: "w15:tentative"
tentative: "w15:tentative",
}, properties);
}
}
@ -23,7 +22,7 @@ class Start extends XmlComponent {
constructor(value: number) {
super("w:start");
this.root.push(new Attributes({
val: value
val: value,
}));
}
}
@ -33,7 +32,7 @@ class NumberFormat extends XmlComponent {
constructor(value: string) {
super("w:numFmt");
this.root.push(new Attributes({
val: value
val: value,
}));
}
}
@ -43,7 +42,7 @@ class LevelText extends XmlComponent {
constructor(value: string) {
super("w:lvlText");
this.root.push(new Attributes({
val: value
val: value,
}));
}
}
@ -53,7 +52,7 @@ class LevelJc extends XmlComponent {
constructor(value: string) {
super("w:lvlJc");
this.root.push(new Attributes({
val: value
val: value,
}));
}
}
@ -66,7 +65,7 @@ export class Level extends XmlComponent {
super("w:lvl");
this.root.push(new LevelAttributes({
ilvl: level,
tentative: 1
tentative: 1,
}));
this.root.push(new Start(1));
@ -81,7 +80,7 @@ export class Level extends XmlComponent {
this.root.push(this.runProperties);
}
clearVariables(): void {
public clearVariables(): void {
this.paragraphProperties.clearVariables();
this.runProperties.clearVariables();
@ -89,11 +88,13 @@ export class Level extends XmlComponent {
delete this.runProperties;
}
addParagraphProperty(property: XmlComponent): void {
public addParagraphProperty(property: XmlComponent): Level {
this.paragraphProperties.push(property);
return this;
}
addRunProperty(property: XmlComponent): void {
public addRunProperty(property: XmlComponent): Level {
this.runProperties.push(property);
return this;
}
}

View File

@ -1,11 +1,11 @@
import {XmlComponent, Attributes} from "../docx/xml-components";
import {Attributes, XmlComponent} from "../docx/xml-components";
export class MultiLevelType extends XmlComponent {
constructor(value: string) {
super("w:multiLevelType");
this.root.push(new Attributes({
val: value
val: value,
}));
}
}

View File

@ -1,35 +1,42 @@
import {XmlComponent, Attributes, XmlAttributeComponent} from "../docx/xml-components";
import { Attributes, XmlAttributeComponent, XmlComponent } from "../docx/xml-components";
class AbstractNumId extends XmlComponent {
constructor(value: number) {
super("w:abstractNumId");
this.root.push(new Attributes({
val: value
val: value,
}));
}
}
interface NumAttributesProperties {
interface INumAttributesProperties {
numId: number;
}
class NumAttributes extends XmlAttributeComponent {
constructor(properties: NumAttributesProperties) {
constructor(properties: INumAttributesProperties) {
super({
numId: "w:numId"
numId: "w:numId",
}, properties);
}
}
export class Num extends XmlComponent {
public id: number;
constructor(numId: number, abstractNumId: number) {
super("w:num");
this.root.push(new NumAttributes({
numId: numId
numId: numId,
}));
this.root.push(new AbstractNumId(abstractNumId));
this.id = numId;
}
public clearVariables(): void {
super.clearVariables();
delete this.id;
}
}

View File

@ -1,6 +1,6 @@
import {XmlComponent, XmlAttributeComponent} from "../docx/xml-components";
import {XmlAttributeComponent, XmlComponent} from "../docx/xml-components";
interface RunFontAttributesProperties {
interface IRunFontAttributesProperties {
ascii: string;
hAnsi: string;
hint: string;
@ -8,10 +8,11 @@ interface RunFontAttributesProperties {
class RunFontAttributes extends XmlAttributeComponent {
constructor(properties: RunFontAttributesProperties) {
constructor(properties: IRunFontAttributesProperties) {
super({
left: "w:left",
hanging: "w:hanging"
ascii: "w:ascii",
hAnsi: "w:hAnsi",
hint: "w:hint",
}, properties);
}
}
@ -19,11 +20,11 @@ class RunFontAttributes extends XmlAttributeComponent {
export class RunFonts extends XmlComponent {
constructor(ascii: string, hint: string) {
super("w:ind");
super("w:rFonts");
this.root.push(new RunFontAttributes({
ascii: ascii,
hAnsi: ascii,
hint: hint
hint: hint,
}));
}
}

View File

@ -1,5 +1,7 @@
import * as docx from "../../../docx";
import { assert } from "chai";
import { Formatter } from "../../../export/formatter";
import { Numbering } from "../../../numbering";
import { assert, expect } from "chai";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
@ -113,4 +115,44 @@ describe("Paragraph", () => {
assert.isDefined(newJson.root[0].root[2]);
});
});
describe("#setNumbering", () => {
it("should add list paragraph style to JSON", () => {
const numbering = new Numbering();
const numberedAbstract = numbering.createAbstractNumbering();
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
paragraph.setNumbering(letterNumbering, 0);
let newJson = jsonify(paragraph);
assert.equal(newJson.root[0].root[1].root[0].root.val, "ListParagraph");
});
it("it should add numbered properties", () => {
const numbering = new Numbering();
const numberedAbstract = numbering.createAbstractNumbering();
numberedAbstract.createLevel(0, "lowerLetter", "%1)", "start");
const letterNumbering = numbering.createConcreteNumbering(numberedAbstract);
paragraph.setNumbering(letterNumbering, 0);
const tree = new Formatter().format(paragraph);
console.log(JSON.stringify(tree, null, 2));
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{"_attr": {}},
{"w:pStyle": [{"_attr": {"w:val": "ListParagraph"}}]},
{
"w:numPr": [
{"w:ilvl": [{"_attr": {"w:val": 0}}]},
{"w:numId": [{"_attr": {"w:val": letterNumbering.id}}]}
]
},
],
},
]
})
});
});
});

View File

@ -10,7 +10,7 @@ describe("NumberProperties", () => {
let numberProperties: NumberProperties;
beforeEach(() => {
numberProperties = new NumberProperties();
numberProperties = new NumberProperties(5, 10);
});
describe("#constructor()", () => {
@ -22,11 +22,13 @@ describe("NumberProperties", () => {
it("should create a Page Break with a Indent Level inside", () => {
let newJson = jsonify(numberProperties);
assert.equal(newJson.root[0].rootKey, "w:ilvl");
assert.equal(newJson.root[0].root[0].root.val, 10);
});
it("should create a Page Break with a Number Id inside", () => {
let newJson = jsonify(numberProperties);
assert.equal(newJson.root[1].rootKey, "w:numId");
assert.equal(newJson.root[1].root[0].root.val, 5);
});
});
});

View File

@ -1,21 +1,109 @@
import { assert } from "chai";
import { expect } from "chai";
import { Formatter } from "../export/formatter";
import { Numbering } from "../numbering";
import { AbstractNumbering } from "../numbering/abstract-numbering";
import { Num } from "../numbering/num";
function jsonify(obj: Object) {
let stringifiedJson = JSON.stringify(obj);
return JSON.parse(stringifiedJson);
function jsonify(obj: object) {
return JSON.parse(JSON.stringify(obj));
}
describe("", () => {
describe("Numbering", () => {
let numbering = new Numbering;
let numbering: Numbering;
beforeEach(() => {
numbering = new Numbering();
});
describe("#methodName()", () => {
it("should ", () => {
describe("#constructor", () => {
it("creates a default numbering with one abstract and one concrete instance", () => {
const tree = new Formatter().format(numbering);
expect(Object.keys(tree)).to.deep.equal(["w:numbering"]);
const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]);
expect(abstractNums).to.have.lengthOf(1);
expect(abstractNums[0]["w:abstractNum"]).to.deep.include.members([
{_attr: {"w:abstractNumId": 0, "w15:restartNumberingAfterBreak": 0}},
{"w:multiLevelType": [{_attr: {"w:val": "hybridMultilevel"}}]},
]);
abstractNums.filter((el) => el["w:lvl"]).forEach((el, ix) => {
expect(Object.keys(el)).to.have.lengthOf(1);
expect(Object.keys(el["w:lvl"]).sort()).to.deep.equal([
"_attr", "w:start", "w:lvlJc", "w:numFmt", "w:pPr", "w:rPr",
]);
expect(el["w:lvl"]).to.have.deep.members([
{_attr: {"w:ilvl": ix, "w15:tentative": 1}},
{"w:start": [{_attr: {"w:val": 1}}]},
{"w:lvlJc": [{_attr: {"w:val": "left"}}]},
{"w:numFmt": [{_attr: {"w:val": "bullet"}}]},
]);
// Once chai 4.0.0 lands and #644 is resolved, we can add the following to the test:
// {"w:lvlText": [{"_attr": {"w:val": "•"}}]},
// {"w:rPr": [{"w:rFonts": [{"_attr": {"w:ascii": "Symbol", "w:hAnsi": "Symbol", "w:hint": "default"}}]}]},
// {"w:pPr": [{"_attr": {}},
// {"w:ind": [{"_attr": {"w:left": 720, "w:hanging": 360}}]}]},
});
});
});
describe("#createAbstractNumbering", () => {
it("returns a new AbstractNumbering instance", () => {
const a2 = numbering.createAbstractNumbering();
expect(a2).to.be.instanceof(AbstractNumbering);
});
it("assigns a unique ID to each abstract numbering it creates", () => {
const a2 = numbering.createAbstractNumbering();
const a3 = numbering.createAbstractNumbering();
expect(a2.id).not.to.equal(a3.id);
});
});
describe("#createConcreteNumbering", () => {
it("returns a new Num instance with its abstract ID set to the AbstractNumbering's ID", () => {
const a2 = numbering.createAbstractNumbering();
const n = numbering.createConcreteNumbering(a2);
expect(n).to.be.instanceof(Num);
const tree = new Formatter().format(numbering);
expect(n.id).to.equal(a2.id);
});
it("assigns a unique ID to each concrete numbering it creates", () => {
const a2 = numbering.createAbstractNumbering();
const n = numbering.createConcreteNumbering(a2);
const n2 = numbering.createConcreteNumbering(a2);
expect(n.id).not.to.equal(n2.id);
});
});
});
describe("AbstractNumbering", () => {
it("stores its ID at its .id property", () => {
const abstractNumbering = new AbstractNumbering(5);
expect(abstractNumbering.id).to.equal(5);
});
describe("#createLevel", () => {
it("creates a level with the given characteristics", () => {
const abstractNumbering = new AbstractNumbering(1);
const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)", "end");
const tree = new Formatter().format(level);
expect(tree['w:lvl']).to.include({_attr: {"w:ilvl": 3, "w15:tentative": 1}})
expect(tree['w:lvl']).to.include({"w:start": [{_attr: {"w:val": 1}}]})
expect(tree['w:lvl']).to.include({"w:lvlJc": [{_attr: {"w:val": "end"}}]})
expect(tree['w:lvl']).to.include({"w:numFmt": [{_attr: {"w:val": "lowerLetter"}}]})
expect(tree['w:lvl']).to.include({"w:lvlText": [{"_attr": {"w:val": "%1)"}}]})
});
it("uses 'start' as the default alignment", () => {
const abstractNumbering = new AbstractNumbering(1);
const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)");
const tree = new Formatter().format(level);
expect(tree['w:lvl']).to.include({_attr: {"w:ilvl": 3, "w15:tentative": 1}})
expect(tree['w:lvl']).to.include({"w:start": [{_attr: {"w:val": 1}}]})
expect(tree['w:lvl']).to.include({"w:lvlJc": [{_attr: {"w:val": "start"}}]})
expect(tree['w:lvl']).to.include({"w:numFmt": [{_attr: {"w:val": "lowerLetter"}}]})
expect(tree['w:lvl']).to.include({"w:lvlText": [{"_attr": {"w:val": "%1)"}}]})
});
});
});