Added some unit tests for navLinkBuilder

Because I could not make jest work with any
references to SPFx, I separated the navLinkBuilder
into its own component.
Also, I set collectCoverage to false, as coverage collection is
currently broken and I see no easy way to fix this.

Two tests are ignored, since they describe the issue and are currently
failing.
This commit is contained in:
Nils Andresen 2022-11-02 23:11:17 +00:00
parent 5b2e546d9d
commit 3593de26ac
No known key found for this signature in database
GPG Key ID: 0EAFED177E6A41E9
6 changed files with 557 additions and 29 deletions

View File

@ -0,0 +1 @@
v14.20.0

View File

@ -1,4 +1,5 @@
{ {
"preset": "@voitanos/jest-preset-spfx-react16", "preset": "@voitanos/jest-preset-spfx-react16",
"rootDir": "../src" "rootDir": "../src",
"collectCoverage": false
} }

View File

@ -0,0 +1,268 @@
import { IHierarchyEntry, navLinkBuilder } from "./NavLinkBuilder";
interface IMockLink extends IHierarchyEntry<IMockLink> {
name: string;
}
const DEPTH_NO_COLLAPSABLE_HEADER = 0;
const DEPTH_COLLAPSABLE_HEADER = 1;
describe("The NavLinkBuilder without a preceding collapsable header", () => {
const depth = DEPTH_NO_COLLAPSABLE_HEADER;
const h1 = depth;
const h2 = h1+1;
const h3 = h2+1;
const h4 = h3+1;
it("should add a single item to an empty list", () => {
const lnk: IMockLink = {
name: "xyz",
};
const actual = navLinkBuilder.build([], lnk, h1, depth);
expect(actual).toMatchSnapshot();
});
it("should add a two items on the same level", () => {
const lnk1: IMockLink = {
name: "xyz",
};
const lnk2: IMockLink = {
name: "abc",
};
let actual = navLinkBuilder.build([], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h1, depth);
expect(actual).toMatchSnapshot();
})
it("should add a two items on different levels", () => {
const lnk1: IMockLink = {
name: "xyz",
};
const lnk2: IMockLink = {
name: "abc",
};
let actual = navLinkBuilder.build([], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h2, depth);
expect(actual).toMatchSnapshot();
});
it("should add a two items on the same level and two items on different levels", () => {
const lnk1: IMockLink = {
name: "xyz",
};
const lnk2: IMockLink = {
name: "abc",
};
const lnk11: IMockLink = {
name: "xyz.1",
};
const lnk21: IMockLink = {
name: "abc.1",
};
let actual = navLinkBuilder.build([], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk11, h2, depth);
actual = navLinkBuilder.build(actual, lnk2, h1, depth);
actual = navLinkBuilder.build(actual, lnk21, h2, depth);
expect(actual).toMatchSnapshot();
});
it("should add a four items on different levels", () => {
const lnk1: IMockLink = {
name: "xyz",
};
const lnk2: IMockLink = {
name: "abc",
};
const lnk3: IMockLink = {
name: "def",
};
const lnk4: IMockLink = {
name: "geh",
};
let actual = navLinkBuilder.build([], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h2, depth);
actual = navLinkBuilder.build(actual, lnk3, h3, depth);
actual = navLinkBuilder.build(actual, lnk4, h4, depth);
expect(actual).toMatchSnapshot();
});
it.skip("should not nest two h2", () => {
const lnk1: IMockLink = {
name: "h1",
};
const lnk2: IMockLink = {
name: "h2",
};
const lnk3: IMockLink = {
name: "h2",
};
const lnk4: IMockLink = {
name: "h3",
};
let actual = navLinkBuilder.build([], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h2, depth);
actual = navLinkBuilder.build(actual, lnk3, h2, depth);
actual = navLinkBuilder.build(actual, lnk4, h3, depth);
expect(actual).toMatchSnapshot();
});
it.skip("should not nest two h3", () => {
const lnk0: IMockLink = {
name: "h1",
};
const lnk1: IMockLink = {
name: "h1",
};
const lnk2: IMockLink = {
name: "h2",
};
const lnk3: IMockLink = {
name: "h2",
};
const lnk4: IMockLink = {
name: "h3",
};
const lnk5: IMockLink = {
name: "h3",
};
const lnk6: IMockLink = {
name: "h4",
};
let actual = navLinkBuilder.build([], lnk0, h1, depth);
actual = navLinkBuilder.build(actual, lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h2, depth);
actual = navLinkBuilder.build(actual, lnk3, h2, depth);
actual = navLinkBuilder.build(actual, lnk4, h3, depth);
actual = navLinkBuilder.build(actual, lnk5, h3, depth);
actual = navLinkBuilder.build(actual, lnk6, h4, depth);
expect(actual).toMatchSnapshot();
});
});
describe("The NavLinkBuilder with a collapsable header", () => {
let head: IMockLink;
const depth = DEPTH_COLLAPSABLE_HEADER;
const h1 = depth;
const h2 = h1+1;
const h3 = h2+1;
const h4 = h3+1;
beforeEach(() => {
head = {
name: "head",
};
});
it("should add a single item to the heading", () => {
const lnk: IMockLink = {
name: "xyz",
};
const actual = navLinkBuilder.build([ head ], lnk, h1, depth);
expect(actual).toMatchSnapshot();
});
it("should add a two items on the same level", () => {
const lnk1: IMockLink = {
name: "xyz",
};
const lnk2: IMockLink = {
name: "abc",
};
let actual = navLinkBuilder.build([ head ], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h1, depth);
expect(actual).toMatchSnapshot();
})
it("should add a one item in a collapsable section two inside that one", () => {
const lnk1: IMockLink = {
name: "xyz",
};
const lnk2: IMockLink = {
name: "abc",
};
const lnk3: IMockLink = {
name: "def",
};
let actual = navLinkBuilder.build([ head ], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h2, depth);
actual = navLinkBuilder.build(actual, lnk3, h2, depth);
expect(actual).toMatchSnapshot();
})
it("should not nest two h2", () => {
const lnk1: IMockLink = {
name: "h1",
};
const lnk2: IMockLink = {
name: "h2",
};
const lnk3: IMockLink = {
name: "h2",
};
const lnk4: IMockLink = {
name: "h3",
};
let actual = navLinkBuilder.build([ head ], lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h2, depth);
actual = navLinkBuilder.build(actual, lnk3, h2, depth);
actual = navLinkBuilder.build(actual, lnk4, h3, depth);
expect(actual).toMatchSnapshot();
});
it("should not nest two h3", () => {
const lnk0: IMockLink = {
name: "h1",
};
const lnk1: IMockLink = {
name: "h1",
};
const lnk2: IMockLink = {
name: "h2",
};
const lnk3: IMockLink = {
name: "h2",
};
const lnk4: IMockLink = {
name: "h3",
};
const lnk5: IMockLink = {
name: "h3",
};
const lnk6: IMockLink = {
name: "h4",
};
let actual = navLinkBuilder.build([ head ], lnk0, h1, depth);
actual = navLinkBuilder.build(actual, lnk1, h1, depth);
actual = navLinkBuilder.build(actual, lnk2, h2, depth);
actual = navLinkBuilder.build(actual, lnk3, h2, depth);
actual = navLinkBuilder.build(actual, lnk4, h3, depth);
actual = navLinkBuilder.build(actual, lnk5, h3, depth);
actual = navLinkBuilder.build(actual, lnk6, h4, depth);
expect(actual).toMatchSnapshot();
});
});

View File

@ -0,0 +1,37 @@
export interface IHierarchyEntry<T extends IHierarchyEntry<T>> {
links?: IHierarchyEntry<T>[];
}
export class navLinkBuilder {
/**
* Nests a new nav link within the nav links tree
* @param currentLinks current nav links
* @param newLink the new nav link to be added to the structure
* @param order place order of the new link
* @param depth sequence depth
* @returns navLinks
*/
public static build<T extends IHierarchyEntry<T>>(currentLinks: T[], newLink: T, order: number, depth: number): T[] {
const lastIndex = currentLinks.length - 1;
if (lastIndex === -1) {
return [newLink];
}
const lastTopLevelLink = currentLinks[lastIndex];
lastTopLevelLink.links = lastTopLevelLink.links || [];
if (lastTopLevelLink.links.length === 0 || order === depth) {
if (order !== depth || depth !== 0) {
lastTopLevelLink.links.push(newLink);
} else {
currentLinks.push(newLink);
}
} else {
depth++;
currentLinks[lastIndex].links.concat(this.build(currentLinks[lastIndex].links, newLink, order, depth));
}
return currentLinks;
}
}

View File

@ -1,6 +1,7 @@
import { INavLink } from 'office-ui-fabric-react/lib/Nav'; import { INavLink } from 'office-ui-fabric-react/lib/Nav';
import { WebPartContext } from '@microsoft/sp-webpart-base'; import { WebPartContext } from '@microsoft/sp-webpart-base';
import { SPHttpClient } from '@microsoft/sp-http'; import { SPHttpClient } from '@microsoft/sp-http';
import { navLinkBuilder } from './NavLinkBuilder';
export class SPService { export class SPService {
/* Array to store all unique anchor URLs */ /* Array to store all unique anchor URLs */
@ -37,33 +38,6 @@ export class SPService {
return anchorUrl; return anchorUrl;
} }
/**
* Nests a new nav link within the nav links tree
* @param currentLinks current nav links
* @param newLink the new nav link to be added to the structure
* @param order place order of the new link
* @param depth sequence depth
* @returns navLinks
*/
public static navLinkBuilder(currentLinks: INavLink[], newLink: INavLink, order: number, depth: number): INavLink[] {
const lastIndex = currentLinks.length - 1;
if (lastIndex === -1) {
currentLinks.push(newLink);
} else if (currentLinks[lastIndex].links.length === 0 || order === depth) {
if (order !== depth || depth !== 0) {
currentLinks[lastIndex].links.push(newLink);
} else {
currentLinks.push(newLink);
}
} else {
depth++;
currentLinks[lastIndex].links.concat(this.navLinkBuilder(currentLinks[lastIndex].links, newLink, order, depth));
}
return currentLinks;
}
/** /**
* Returns the Anchor Links for Nav element * Returns the Anchor Links for Nav element
* @param context Web part context * @param context Web part context
@ -126,7 +100,7 @@ export class SPService {
// Add link to nav element // Add link to nav element
const newNavLink: INavLink = { name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }; const newNavLink: INavLink = { name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true };
anchorLinks = this.navLinkBuilder(anchorLinks, newNavLink, headingOrder, hasCollapsableHeader ? 1 : 0); anchorLinks = navLinkBuilder.build<INavLink>(anchorLinks, newNavLink, headingOrder, hasCollapsableHeader ? 1 : 0);
}); });
} }
}); });

View File

@ -0,0 +1,247 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`The NavLinkBuilder with a collapsable header should add a one item in a collapsable section two inside that one 1`] = `
Array [
Object {
"links": Array [
Object {
"links": Array [
Object {
"name": "abc",
},
Object {
"name": "def",
},
],
"name": "xyz",
},
],
"name": "head",
},
]
`;
exports[`The NavLinkBuilder with a collapsable header should add a single item to the heading 1`] = `
Array [
Object {
"links": Array [
Object {
"name": "xyz",
},
],
"name": "head",
},
]
`;
exports[`The NavLinkBuilder with a collapsable header should add a two items on the same level 1`] = `
Array [
Object {
"links": Array [
Object {
"name": "xyz",
},
Object {
"name": "abc",
},
],
"name": "head",
},
]
`;
exports[`The NavLinkBuilder with a collapsable header should not nest two h2 1`] = `
Array [
Object {
"links": Array [
Object {
"links": Array [
Object {
"name": "h2",
},
Object {
"links": Array [
Object {
"name": "h3",
},
],
"name": "h2",
},
],
"name": "h1",
},
],
"name": "head",
},
]
`;
exports[`The NavLinkBuilder with a collapsable header should not nest two h3 1`] = `
Array [
Object {
"links": Array [
Object {
"name": "h1",
},
Object {
"links": Array [
Object {
"name": "h2",
},
Object {
"links": Array [
Object {
"name": "h3",
},
Object {
"links": Array [
Object {
"name": "h4",
},
],
"name": "h3",
},
],
"name": "h2",
},
],
"name": "h1",
},
],
"name": "head",
},
]
`;
exports[`The NavLinkBuilder without a preceding collapsable header should add a four items on different levels 1`] = `
Array [
Object {
"links": Array [
Object {
"links": Array [
Object {
"links": Array [
Object {
"name": "geh",
},
],
"name": "def",
},
],
"name": "abc",
},
],
"name": "xyz",
},
]
`;
exports[`The NavLinkBuilder without a preceding collapsable header should add a single item to an empty list 1`] = `
Array [
Object {
"name": "xyz",
},
]
`;
exports[`The NavLinkBuilder without a preceding collapsable header should add a two items on different levels 1`] = `
Array [
Object {
"links": Array [
Object {
"name": "abc",
},
],
"name": "xyz",
},
]
`;
exports[`The NavLinkBuilder without a preceding collapsable header should add a two items on the same level 1`] = `
Array [
Object {
"links": Array [],
"name": "xyz",
},
Object {
"name": "abc",
},
]
`;
exports[`The NavLinkBuilder without a preceding collapsable header should add a two items on the same level and two items on different levels 1`] = `
Array [
Object {
"links": Array [
Object {
"name": "xyz.1",
},
],
"name": "xyz",
},
Object {
"links": Array [
Object {
"name": "abc.1",
},
],
"name": "abc",
},
]
`;
exports[`The NavLinkBuilder without a preceding collapsable header should not nest two h2 1`] = `
Array [
Object {
"links": Array [
Object {
"links": Array [],
"name": "h2",
},
Object {
"links": Array [
Object {
"name": "h3",
},
],
"name": "h2",
},
],
"name": "h1",
},
]
`;
exports[`The NavLinkBuilder without a preceding collapsable header should not nest two h3 1`] = `
Array [
Object {
"links": Array [],
"name": "h1",
},
Object {
"links": Array [
Object {
"links": Array[],
"name": "h2",
},
Object {
"links": Array [
Object {
"name": "h3",
},
Object {
"links": Array [
Object {
"name": "h4",
},
],
"name": "h3",
},
],
"name": "h2",
},
],
"name": "h1",
},
]
`;