|
|
|
|
@ -1,6 +1,9 @@
|
|
|
|
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
|
|
|
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
|
|
|
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
|
|
|
|
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
|
|
|
|
|
|
|
|
|
import { ImportResult } from "../models";
|
|
|
|
|
|
|
|
|
|
import { BaseImporter } from "./base-importer";
|
|
|
|
|
|
|
|
|
|
@ -16,8 +19,169 @@ class FakeBaseImporter extends BaseImporter {
|
|
|
|
|
parseXml(data: string): Document {
|
|
|
|
|
return super.parseXml(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
processFolder(result: ImportResult, folderName: string, addRelationship: boolean = true): void {
|
|
|
|
|
return super.processFolder(result, folderName, addRelationship);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe("processFolder method", () => {
|
|
|
|
|
let result: ImportResult;
|
|
|
|
|
const importer = new FakeBaseImporter();
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
result = {
|
|
|
|
|
folders: [],
|
|
|
|
|
folderRelationships: [],
|
|
|
|
|
collections: [],
|
|
|
|
|
collectionRelationships: [],
|
|
|
|
|
ciphers: [],
|
|
|
|
|
success: false,
|
|
|
|
|
errorMessage: "",
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should add a new folder and relationship when folderName is unique", () => {
|
|
|
|
|
// arrange
|
|
|
|
|
// a folder exists - but it is not the same as the one we are importing
|
|
|
|
|
result = {
|
|
|
|
|
folders: [{ name: "ABC" } as FolderView],
|
|
|
|
|
folderRelationships: [],
|
|
|
|
|
collections: [],
|
|
|
|
|
collectionRelationships: [],
|
|
|
|
|
ciphers: [{ name: "cipher1", id: "cipher1" } as CipherView],
|
|
|
|
|
success: false,
|
|
|
|
|
errorMessage: "",
|
|
|
|
|
};
|
|
|
|
|
importer.processFolder(result, "Folder1");
|
|
|
|
|
|
|
|
|
|
expect(result.folders).toHaveLength(2);
|
|
|
|
|
expect(result.folders[0].name).toBe("ABC");
|
|
|
|
|
expect(result.folders[1].name).toBe("Folder1");
|
|
|
|
|
expect(result.folderRelationships).toHaveLength(1);
|
|
|
|
|
expect(result.folderRelationships[0]).toEqual([1, 1]); // cipher1 -> Folder1
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should not add duplicate folders and should add relationships", () => {
|
|
|
|
|
// setup
|
|
|
|
|
// folder called "Folder1" already exists
|
|
|
|
|
result = {
|
|
|
|
|
folders: [{ name: "Folder1" } as FolderView],
|
|
|
|
|
folderRelationships: [],
|
|
|
|
|
collections: [],
|
|
|
|
|
collectionRelationships: [],
|
|
|
|
|
ciphers: [{ name: "cipher1", id: "cipher1" } as CipherView],
|
|
|
|
|
success: false,
|
|
|
|
|
errorMessage: "",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// import an existing folder should not add to the result.folders
|
|
|
|
|
importer.processFolder(result, "Folder1");
|
|
|
|
|
|
|
|
|
|
expect(result.folders).toHaveLength(1);
|
|
|
|
|
expect(result.folders[0].name).toBe("Folder1");
|
|
|
|
|
expect(result.folderRelationships).toHaveLength(1);
|
|
|
|
|
expect(result.folderRelationships[0]).toEqual([1, 0]); // cipher1 -> folder1
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should create parent folders for nested folder names but not duplicates", () => {
|
|
|
|
|
// arrange
|
|
|
|
|
result = {
|
|
|
|
|
folders: [
|
|
|
|
|
{ name: "Ancestor/Parent/Child" } as FolderView,
|
|
|
|
|
{ name: "Ancestor" } as FolderView,
|
|
|
|
|
],
|
|
|
|
|
folderRelationships: [],
|
|
|
|
|
collections: [],
|
|
|
|
|
collectionRelationships: [],
|
|
|
|
|
ciphers: [{ name: "cipher1", id: "cipher1" } as CipherView],
|
|
|
|
|
success: false,
|
|
|
|
|
errorMessage: "",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// act
|
|
|
|
|
// importing an existing folder with a relationship should not change the result.folders
|
|
|
|
|
// nor should it change the result.folderRelationships
|
|
|
|
|
importer.processFolder(result, "Ancestor/Parent/Child/Grandchild/GreatGrandchild");
|
|
|
|
|
|
|
|
|
|
expect(result.folders).toHaveLength(5);
|
|
|
|
|
expect(result.folders.map((f) => f.name)).toEqual([
|
|
|
|
|
"Ancestor/Parent/Child",
|
|
|
|
|
"Ancestor",
|
|
|
|
|
"Ancestor/Parent/Child/Grandchild/GreatGrandchild",
|
|
|
|
|
"Ancestor/Parent/Child/Grandchild",
|
|
|
|
|
"Ancestor/Parent",
|
|
|
|
|
]);
|
|
|
|
|
expect(result.folderRelationships).toHaveLength(1);
|
|
|
|
|
expect(result.folderRelationships[0]).toEqual([1, 2]); // cipher1 -> grandchild
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should not affect existing relationships", () => {
|
|
|
|
|
// arrange
|
|
|
|
|
// "Parent" is a folder with no relationship
|
|
|
|
|
// "Child" is a folder with 2 ciphers
|
|
|
|
|
result = {
|
|
|
|
|
folders: [{ name: "Parent" } as FolderView, { name: "Parent/Child" } as FolderView],
|
|
|
|
|
folderRelationships: [
|
|
|
|
|
[1, 1],
|
|
|
|
|
[2, 1],
|
|
|
|
|
],
|
|
|
|
|
collections: [],
|
|
|
|
|
collectionRelationships: [],
|
|
|
|
|
ciphers: [
|
|
|
|
|
{ name: "cipher1", id: "cipher1" } as CipherView,
|
|
|
|
|
{ name: "cipher2", id: "cipher2" } as CipherView,
|
|
|
|
|
{ name: "cipher3", id: "cipher3" } as CipherView,
|
|
|
|
|
],
|
|
|
|
|
success: false,
|
|
|
|
|
errorMessage: "",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// act
|
|
|
|
|
// importing an existing folder with a relationship should not change the result.folders
|
|
|
|
|
// nor should it change the result.folderRelationships
|
|
|
|
|
importer.processFolder(result, "Parent/Child/Grandchild");
|
|
|
|
|
|
|
|
|
|
expect(result.folders).toHaveLength(3);
|
|
|
|
|
expect(result.folders.map((f) => f.name)).toEqual([
|
|
|
|
|
"Parent",
|
|
|
|
|
"Parent/Child",
|
|
|
|
|
"Parent/Child/Grandchild",
|
|
|
|
|
]);
|
|
|
|
|
expect(result.folderRelationships).toHaveLength(3);
|
|
|
|
|
expect(result.folderRelationships[0]).toEqual([1, 1]); // cipher1 -> child
|
|
|
|
|
expect(result.folderRelationships[1]).toEqual([2, 1]); // cipher2 -> child
|
|
|
|
|
expect(result.folderRelationships[2]).toEqual([3, 2]); // cipher3 -> grandchild
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should not add relationships if addRelationship is false", () => {
|
|
|
|
|
importer.processFolder(result, "Folder1", false);
|
|
|
|
|
|
|
|
|
|
expect(result.folders).toHaveLength(1);
|
|
|
|
|
expect(result.folders[0].name).toBe("Folder1");
|
|
|
|
|
expect(result.folderRelationships).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should replace backslashes with forward slashes in folder names", () => {
|
|
|
|
|
importer.processFolder(result, "Parent\\Child\\Grandchild");
|
|
|
|
|
|
|
|
|
|
expect(result.folders).toHaveLength(3);
|
|
|
|
|
expect(result.folders.map((f) => f.name)).toEqual([
|
|
|
|
|
"Parent/Child/Grandchild",
|
|
|
|
|
"Parent/Child",
|
|
|
|
|
"Parent",
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should handle empty or null folder names gracefully", () => {
|
|
|
|
|
importer.processFolder(result, null);
|
|
|
|
|
importer.processFolder(result, "");
|
|
|
|
|
|
|
|
|
|
expect(result.folders).toHaveLength(0);
|
|
|
|
|
expect(result.folderRelationships).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("BaseImporter class", () => {
|
|
|
|
|
const importer = new FakeBaseImporter();
|
|
|
|
|
let cipher: CipherView;
|
|
|
|
|
|