Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/tests/unit/confluence/confluence.test.ts
6451 views
1
/*
2
*
3
* Copyright (C) 2020 by RStudio, PBC
4
*
5
*/
6
import { unitTest } from "../../test.ts";
7
import { assertEquals, assertThrows } from "testing/asserts";
8
9
import {
10
buildContentCreate,
11
buildFileToMetaTable,
12
buildPublishRecordForContent,
13
buildSpaceChanges,
14
capitalizeFirstLetter,
15
confluenceParentFromString,
16
convertForSecondPass,
17
FILE_FINDER,
18
filterFilesForUpdate,
19
findAttachments,
20
findPagesToDelete,
21
flattenIndexes,
22
footnoteTransform,
23
getMessageFromAPIError,
24
getNextVersion,
25
getTitle,
26
isNotFound,
27
isUnauthorized,
28
LINK_FINDER,
29
mergeSitePages,
30
tokenFilterOut,
31
transformAtlassianDomain,
32
updateImagePaths,
33
updateLinks,
34
validateEmail,
35
validateServer,
36
validateToken,
37
wrapBodyForConfluence,
38
writeTokenComparator,
39
} from "../../../src/publish/confluence/confluence-helper.ts";
40
import { ApiError, PublishRecord } from "../../../src/publish/types.ts";
41
import {
42
AccountToken,
43
AccountTokenType,
44
InputMetadata,
45
} from "../../../src/publish/provider-types.ts";
46
import {
47
ConfluenceParent,
48
ConfluenceSpaceChange,
49
Content,
50
ContentBody,
51
ContentChangeType,
52
ContentCreate,
53
ContentStatusEnum,
54
ContentSummary,
55
ContentUpdate,
56
ContentVersion,
57
ExtractedLink,
58
PAGE_TYPE,
59
SiteFileMetadata,
60
SitePage,
61
Space,
62
} from "../../../src/publish/confluence/api/types.ts";
63
64
const RUN_ALL_TESTS = true;
65
const FOCUS_TEST = false;
66
const HIDE_NOISE = false;
67
68
const xtest = (
69
name: string,
70
ver: () => Promise<unknown> // VoidFunction,
71
) => {};
72
const test = FOCUS_TEST ? xtest : unitTest;
73
const otest = unitTest;
74
75
const buildFakeContent = (): Content => {
76
return {
77
id: "fake-id",
78
type: "fake-type",
79
status: "current",
80
title: "fake-title",
81
space: {
82
key: "fake-space-key",
83
id: "fake-space-id",
84
homepage: {
85
id: "fake-space-id",
86
title: "fake-space-title",
87
},
88
},
89
version: {
90
number: 1,
91
},
92
ancestors: null,
93
descendants: null,
94
body: {
95
storage: {
96
value: "fake-body",
97
representation: "raw",
98
},
99
},
100
};
101
};
102
103
const FAKE_PARENT: ConfluenceParent = {
104
space: "QUARTOCONF",
105
parent: "8781825",
106
};
107
108
const runGeneralTests = () => {
109
test("transformAtlassianDomain_basic", async () => {
110
const result = transformAtlassianDomain("fake-domain");
111
const expected = "https://fake-domain.atlassian.net/";
112
assertEquals(expected, result);
113
});
114
115
test("transformAtlassianDomain_EmptyString", async () => {
116
const result = transformAtlassianDomain("");
117
const expected = "https://.atlassian.net/";
118
assertEquals(expected, result);
119
});
120
121
test("transformAtlassianDomain_addTrailing", async () => {
122
const result = transformAtlassianDomain("https://something");
123
const expected = "https://something/";
124
assertEquals(expected, result);
125
});
126
127
test("transformAtlassianDomain_partialPrefix", async () => {
128
const result = transformAtlassianDomain("htt://something");
129
const expected = "https://htt://something.atlassian.net/";
130
assertEquals(expected, result);
131
});
132
133
test("transformAtlassianDomain_addPrefixAndTrailing", async () => {
134
const result = transformAtlassianDomain("something");
135
const expected = "https://something.atlassian.net/";
136
assertEquals(expected, result);
137
});
138
139
test("validateServer_empty", async () => {
140
const toCall = () => validateServer("");
141
assertThrows(toCall, "");
142
});
143
144
test("validateServer_valid", async () => {
145
const result = validateServer("fake-domain");
146
const expected = true;
147
assertEquals(expected, result);
148
});
149
150
test("validateServer_invalid", async () => {
151
const result = validateServer("_!@ ... #");
152
const expected = "Not a valid URL";
153
assertEquals(expected, result);
154
});
155
156
test("validateName_empty", async () => {
157
const toCall = () => validateEmail("");
158
assertThrows(toCall, "");
159
});
160
161
test("validateName_valid", async () => {
162
const result = validateEmail("[email protected]");
163
const expected = true;
164
assertEquals(expected, result);
165
});
166
167
test("validateName_invalid_JustName", async () => {
168
const result = validateEmail("al.manning");
169
const expected = "Invalid email address";
170
assertEquals(expected, result);
171
});
172
173
test("validateToken_empty", async () => {
174
const toCall = () => validateToken("");
175
assertThrows(toCall, "");
176
});
177
178
test("getMessageFromAPIError_null", async () => {
179
const result = getMessageFromAPIError(null);
180
const expected = "Unknown error";
181
assertEquals(expected, result);
182
});
183
184
test("getMessageFromAPIError_emptyString", async () => {
185
const result = getMessageFromAPIError("");
186
const expected = "Unknown error";
187
assertEquals(expected, result);
188
});
189
190
test("getMessageFromAPIError_APIError", async () => {
191
const result = getMessageFromAPIError(new ApiError(123, "status-text"));
192
const expected = "123 - status-text";
193
assertEquals(expected, result);
194
});
195
196
test("tokenFilterOut_sameToken", async () => {
197
const fakeToken = {
198
type: AccountTokenType.Environment,
199
name: "fake-name",
200
server: "fake-server",
201
token: "fake-token",
202
};
203
204
const result = tokenFilterOut(fakeToken, fakeToken);
205
const expected = false;
206
assertEquals(expected, result);
207
});
208
209
test("tokenFilterOut_differentToken", async () => {
210
const fakeToken = {
211
type: AccountTokenType.Environment,
212
name: "fake-name",
213
server: "fake-server",
214
token: "fake-token",
215
};
216
217
const fakeToken2 = {
218
type: AccountTokenType.Environment,
219
name: "fake-name2",
220
server: "fake-server2",
221
token: "fake-token2",
222
};
223
224
const result = tokenFilterOut(fakeToken, fakeToken2);
225
const expected = true;
226
assertEquals(expected, result);
227
});
228
229
test("isUnauthorized_EmptyError", async () => {
230
const result = isUnauthorized(new Error());
231
const expected = false;
232
assertEquals(expected, result);
233
});
234
235
test("isUnauthorized_401", async () => {
236
const result = isUnauthorized(new ApiError(401, "fake-status"));
237
const expected = true;
238
assertEquals(expected, result);
239
});
240
241
test("isUnauthorized_403", async () => {
242
const result = isUnauthorized(new ApiError(403, "fake-status"));
243
const expected = true;
244
assertEquals(expected, result);
245
});
246
247
test("isNotFound_Empty", async () => {
248
const result = isNotFound(new Error());
249
const expected = false;
250
assertEquals(expected, result);
251
});
252
253
test("isNotFound_404", async () => {
254
const result = isNotFound(new ApiError(404, "fake-status"));
255
const expected = true;
256
assertEquals(expected, result);
257
});
258
};
259
260
const runConfluenceParentFromString = () => {
261
test("confluenceParentFromString_empty", async () => {
262
const result = confluenceParentFromString("");
263
const expected = {
264
space: "",
265
parent: "",
266
};
267
assertEquals(expected, result);
268
});
269
270
test("confluenceParentFromString_valid", async () => {
271
const url =
272
"https://allenmanning.atlassian.net/wiki/spaces/QUARTOCONF/pages/8781825/Markdown+Basics1";
273
const result = confluenceParentFromString(url);
274
const expected: ConfluenceParent = {
275
space: "QUARTOCONF",
276
parent: "8781825",
277
};
278
assertEquals(expected, result);
279
});
280
281
test("confluenceParentFromString_valid_noParent", async () => {
282
const url = "https://allenmanning.atlassian.net/wiki/spaces/QUARTOCONF";
283
const result = confluenceParentFromString(url);
284
const expected: ConfluenceParent = {
285
space: "QUARTOCONF",
286
parent: undefined,
287
};
288
assertEquals(expected, result);
289
});
290
291
test("confluenceParentFromString_spaces_overview", async () => {
292
const url =
293
"https://allenmanning.atlassian.net/wiki/spaces/~557058634d59d0949841909bb13093ab41d0c5/overview";
294
const result = confluenceParentFromString(url);
295
const expected: ConfluenceParent = {
296
space: "~557058634d59d0949841909bb13093ab41d0c5",
297
parent: undefined,
298
};
299
assertEquals(expected, result);
300
});
301
302
test("confluenceParentFromString_valid_spaces_pages", async () => {
303
const url =
304
"https://rstudiopbc-sandbox-249.atlassian.net/wiki/spaces/~62d7a66910c44eb6e3218195/pages/43122955";
305
const result = confluenceParentFromString(url);
306
const expected: ConfluenceParent = {
307
space: "~62d7a66910c44eb6e3218195",
308
parent: "43122955",
309
};
310
assertEquals(expected, result);
311
});
312
313
test("confluenceParentFromString_valid_spaces_pages_with_homepage", async () => {
314
const url =
315
"https://allenmanning.atlassian.net/wiki/spaces/~557058634d59d0949841909bb13093ab41d0c5/overview?homepageId=65617";
316
const result = confluenceParentFromString(url);
317
const expected: ConfluenceParent = {
318
space: "~557058634d59d0949841909bb13093ab41d0c5",
319
parent: undefined,
320
};
321
assertEquals(expected, result);
322
});
323
324
test("confluenceParentFromString_valid_noParent", async () => {
325
const url = "https://test.atlassian.net/wiki/spaces/QUARTOCONF";
326
const result = confluenceParentFromString(url);
327
const expected: ConfluenceParent = {
328
space: "QUARTOCONF",
329
parent: undefined,
330
};
331
assertEquals(expected, result);
332
});
333
334
test("confluenceParentFromString_valid_space_dots", async () => {
335
// https://github.com/quarto-dev/quarto-cli/issues/5405
336
const url = "https://test.atlassian.net/wiki/spaces/~brian.smith/pages/126583477";
337
const result = confluenceParentFromString(url);
338
const expected: ConfluenceParent = {
339
space: "~brian.smith",
340
parent: "126583477",
341
};
342
assertEquals(expected, result);
343
});
344
345
test("confluenceParentFromString_invalid_noSpace", async () => {
346
const url = "https://test.atlassian.net/QUARTOCONF";
347
const result = confluenceParentFromString(url);
348
const expected = {
349
space: "",
350
parent: "",
351
};
352
assertEquals(expected, result);
353
});
354
355
test("wrapBodyForConfluence_empty", async () => {
356
const value = "";
357
const result = wrapBodyForConfluence(value);
358
const expected = {
359
storage: {
360
value: "",
361
representation: "storage",
362
},
363
};
364
assertEquals(expected, result);
365
});
366
};
367
368
const runPublishRecordTests = () => {
369
const fakeServer = "https://test.atlassian.net";
370
371
const checkForContent = (
372
expectedURL: string,
373
expectedId: string,
374
server: string = fakeServer,
375
content: Content = buildFakeContent()
376
) => {
377
const result = buildPublishRecordForContent(server, content);
378
const expectedPublishRecord: PublishRecord = {
379
id: expectedId,
380
url: expectedURL,
381
};
382
const url: URL = new URL(expectedURL);
383
const expected: [PublishRecord, URL] = [expectedPublishRecord, url];
384
385
assertEquals(expected[0], result[0]);
386
assertEquals(expected[1], result[1]);
387
};
388
389
const checkThrows = (
390
server: string = fakeServer,
391
content: Content = buildFakeContent()
392
) => {
393
assertThrows(() => buildPublishRecordForContent(server, content));
394
};
395
396
test("buildPublishRecord_validWithChecker", async () => {
397
const expectedURL =
398
"https://test.atlassian.net/wiki/spaces/fake-space-key/pages/fake-id";
399
const expectedId = "fake-id";
400
401
checkForContent(expectedURL, expectedId);
402
});
403
404
test("buildPublishRecord_noIdThrows", async () => {
405
const fakeContent = buildFakeContent();
406
fakeContent.id = null;
407
checkThrows(fakeServer, fakeContent);
408
});
409
410
test("buildPublishRecord_noSpaceThrows", async () => {
411
const fakeContent = buildFakeContent();
412
fakeContent.space = null;
413
checkThrows(fakeServer, fakeContent);
414
});
415
416
test("buildPublishRecord_emptyServerThrows", async () => {
417
checkThrows("");
418
});
419
420
const checkForParent = (
421
expectedURL: string,
422
expectedId: string,
423
server: string = fakeServer,
424
content: Content = buildFakeContent()
425
) => {
426
const result = buildPublishRecordForContent(server, content);
427
const expectedPublishRecord: PublishRecord = {
428
id: expectedId,
429
url: expectedURL,
430
};
431
const url: URL = new URL(expectedURL);
432
const expected: [PublishRecord, URL] = [expectedPublishRecord, url];
433
434
assertEquals(expected[0], result[0]);
435
assertEquals(expected[1], result[1]);
436
};
437
};
438
439
const runGetNextVersionTests = () => {
440
const suiteLabel = (label: string) => `GetNextVersionTests_${label}`;
441
442
const check = (previousPage: Content, expected: ContentVersion) => {
443
const actual = getNextVersion(previousPage);
444
assertEquals(expected, actual);
445
};
446
447
test(suiteLabel("1to2"), async () => {
448
const previousPage: Content = buildFakeContent();
449
const expected: ContentVersion = { number: 2 };
450
check(previousPage, expected);
451
});
452
453
test(suiteLabel("Nullto1"), async () => {
454
const previousPage: Content = buildFakeContent();
455
previousPage.version = null;
456
const expected: ContentVersion = { number: 1 };
457
check(previousPage, expected);
458
});
459
};
460
461
const runWriteTokenComparator = () => {
462
const suiteLabel = (label: string) => `WriteTokenComparator_${label}`;
463
464
const check = (
465
aToken: AccountToken,
466
bToken: AccountToken,
467
expected: boolean
468
) => {
469
const actual = writeTokenComparator(aToken, bToken);
470
assertEquals(expected, actual);
471
};
472
473
test(suiteLabel("allNotEqual"), async () => {
474
check(
475
{
476
server: "a-server",
477
name: "a-name",
478
type: AccountTokenType.Authorized,
479
token: "fake-token-a",
480
},
481
{
482
server: "b-server",
483
name: "b-name",
484
type: AccountTokenType.Environment,
485
token: "fake-token-b",
486
},
487
false
488
);
489
});
490
491
test(suiteLabel("nameNotEqual"), async () => {
492
check(
493
{
494
server: "a-server",
495
name: "different-a-name",
496
type: AccountTokenType.Authorized,
497
token: "fake-token-a",
498
},
499
{
500
server: "a-server",
501
name: "a-name",
502
type: AccountTokenType.Authorized,
503
token: "fake-token-a",
504
},
505
false
506
);
507
});
508
509
test(suiteLabel("serverNotEqual"), async () => {
510
check(
511
{
512
server: "different-a-server",
513
name: "a-name",
514
type: AccountTokenType.Authorized,
515
token: "fake-token-a",
516
},
517
{
518
server: "a-server",
519
name: "a-name",
520
type: AccountTokenType.Authorized,
521
token: "fake-token-a",
522
},
523
false
524
);
525
});
526
527
test(suiteLabel("typeNotEqual"), async () => {
528
check(
529
{
530
server: "a-server",
531
name: "a-name",
532
type: AccountTokenType.Environment,
533
token: "fake-token-a",
534
},
535
{
536
server: "a-server",
537
name: "a-name",
538
type: AccountTokenType.Authorized,
539
token: "fake-token-a",
540
},
541
true
542
);
543
});
544
545
test(suiteLabel("tokenNotEqual"), async () => {
546
check(
547
{
548
server: "a-server",
549
name: "a-name",
550
type: AccountTokenType.Authorized,
551
token: "differet-fake-token-a",
552
},
553
{
554
server: "a-server",
555
name: "a-name",
556
type: AccountTokenType.Authorized,
557
token: "fake-token-a",
558
},
559
true
560
);
561
});
562
};
563
564
const runFilterFilesForUpdate = () => {
565
const suiteLabel = (label: string) => `FilterFilesForUpdate_${label}`;
566
567
const check = (allFiles: string[], expected: string[]) => {
568
const actual = filterFilesForUpdate(allFiles);
569
assertEquals(expected, actual);
570
};
571
572
test(suiteLabel("noFiles"), async () => {
573
check([], []);
574
});
575
576
test(suiteLabel("flatMixed"), async () => {
577
const fakeFileList = [
578
"knowledge-base.html",
579
"team.xml",
580
"agreements.html",
581
"mission.xml",
582
"ci-log.html",
583
];
584
const expected = ["team.xml", "mission.xml"];
585
586
check(fakeFileList, expected);
587
});
588
589
test(suiteLabel("nestedMixed"), async () => {
590
const fakeFileList = [
591
"parent/child.xml",
592
"knowledge-base.html",
593
"team.xml",
594
"agreements.html",
595
"mission.xml",
596
"ci-log.html",
597
];
598
const expected = ["parent/child.xml", "team.xml", "mission.xml"];
599
600
check(fakeFileList, expected);
601
});
602
};
603
604
const runBuildContentCreate = () => {
605
const suiteLabel = (label: string) => `BuildContentCreate_${label}`;
606
607
test(suiteLabel("minParams"), async () => {
608
const expected: ContentCreate = {
609
contentChangeType: ContentChangeType.create,
610
fileName: "fake-file-name",
611
title: "fake-title",
612
type: PAGE_TYPE,
613
space: {
614
key: "fake-space-key",
615
id: "fake-space-id",
616
homepage: buildFakeContent(),
617
},
618
status: ContentStatusEnum.current,
619
ancestors: null,
620
body: {
621
storage: {
622
value: "fake-value",
623
representation: "storage",
624
},
625
},
626
};
627
const fakeSpace: Space = {
628
key: "fake-space-key",
629
id: "fake-space-id",
630
homepage: buildFakeContent(),
631
};
632
const fakeBody: ContentBody = {
633
storage: {
634
value: "fake-value",
635
representation: "storage",
636
},
637
};
638
const actual: ContentCreate = buildContentCreate(
639
"fake-title",
640
fakeSpace,
641
fakeBody,
642
"fake-file-name"
643
);
644
645
assertEquals(expected, actual);
646
});
647
648
test(suiteLabel("allParams"), async () => {
649
const expected: ContentCreate = {
650
contentChangeType: ContentChangeType.create,
651
fileName: "fake-filename",
652
title: "fake-title",
653
type: "fake-type",
654
space: {
655
key: "fake-space-key",
656
id: "fake-space-id",
657
homepage: buildFakeContent(),
658
},
659
status: ContentStatusEnum.deleted,
660
ancestors: [
661
{
662
id: "fake-parent",
663
},
664
],
665
body: {
666
storage: {
667
value: "fake-value",
668
representation: "storage",
669
},
670
},
671
};
672
673
const fakeSpace: Space = {
674
key: "fake-space-key",
675
id: "fake-space-id",
676
homepage: buildFakeContent(),
677
};
678
const fakeBody: ContentBody = {
679
storage: {
680
value: "fake-value",
681
representation: "storage",
682
},
683
};
684
const actual: ContentCreate = buildContentCreate(
685
"fake-title",
686
fakeSpace,
687
fakeBody,
688
"fake-filename",
689
"fake-parent",
690
ContentStatusEnum.deleted,
691
"fake-id",
692
"fake-type"
693
);
694
695
assertEquals(expected, actual);
696
});
697
};
698
699
const runGetTitle = () => {
700
const suiteLabel = (label: string) => `GetTitle_${label}`;
701
const fakeInputMetadata: Record<string, InputMetadata> = {
702
"fake-file.qmd": {
703
title: "fake-title1",
704
author: "fake-author",
705
date: "fake-date",
706
},
707
"folder/fake-file2.qmd": {
708
title: "fake-title2",
709
author: "fake-author2",
710
date: "fake-date2",
711
},
712
};
713
714
test(suiteLabel("valid"), async () => {
715
const fileName = "fake-file.xml";
716
const expected = "fake-title1";
717
const result = getTitle(fileName, fakeInputMetadata);
718
assertEquals(expected, result);
719
});
720
721
test(suiteLabel("valid2"), async () => {
722
const fileName = "folder/fake-file2.xml";
723
const expected = "fake-title2";
724
const result = getTitle(fileName, fakeInputMetadata);
725
assertEquals(expected, result);
726
});
727
728
test(suiteLabel("no-match"), async () => {
729
const fileName = "fake-file3.xml";
730
const expected = "Fake-file3";
731
const result = getTitle(fileName, fakeInputMetadata);
732
assertEquals(expected, result);
733
});
734
735
test(suiteLabel("no-match-empty"), async () => {
736
const fileName = "";
737
const expected = "";
738
const result = getTitle(fileName, fakeInputMetadata);
739
assertEquals(expected, result);
740
});
741
};
742
743
const runMergeSitePages = () => {
744
const suiteLabel = (label: string) => `MergeSitePages_${label}`;
745
746
test(suiteLabel("basic_valid"), async () => {
747
const shallowPages: ContentSummary[] = [
748
{
749
id: "123",
750
title: "fake-title",
751
ancestors: [{ id: "fake-ancestor" }],
752
},
753
];
754
const contentProperties = [
755
[
756
{
757
key: "fake-key",
758
value: "fake-value",
759
},
760
],
761
];
762
const expected: SitePage[] = [
763
{
764
id: "123",
765
title: "fake-title",
766
ancestors: [{ id: "fake-ancestor" }],
767
metadata: {
768
["fake-key"]: "fake-value",
769
},
770
},
771
];
772
const result = mergeSitePages(shallowPages, contentProperties);
773
assertEquals(expected, result);
774
});
775
776
test(suiteLabel("basic_valid_2props"), async () => {
777
const shallowPages: ContentSummary[] = [
778
{
779
id: "123",
780
title: "fake-title",
781
ancestors: [{ id: "fake-ancestor" }],
782
},
783
];
784
const contentProperties = [
785
[
786
{
787
key: "fake-key",
788
value: "fake-value",
789
},
790
{
791
key: "fake-key2",
792
value: "fake-value2",
793
},
794
],
795
];
796
const expected: SitePage[] = [
797
{
798
id: "123",
799
title: "fake-title",
800
metadata: {
801
["fake-key"]: "fake-value",
802
["fake-key2"]: "fake-value2",
803
},
804
ancestors: [{ id: "fake-ancestor" }],
805
},
806
];
807
const result = mergeSitePages(shallowPages, contentProperties);
808
assertEquals(expected, result);
809
});
810
811
test(suiteLabel("multiple_valid"), async () => {
812
const shallowPages: ContentSummary[] = [
813
{
814
id: "123",
815
title: "fake-title",
816
ancestors: [{ id: "fake-ancestor" }],
817
},
818
{
819
id: "456",
820
title: "fake-title2",
821
ancestors: [{ id: "fake-ancestor-2" }],
822
},
823
];
824
const contentProperties = [
825
[
826
{
827
key: "fake-key",
828
value: "fake-value",
829
},
830
],
831
[
832
{
833
key: "fake-key2",
834
value: "fake-value2",
835
},
836
{
837
key: "fake-key3",
838
value: "fake-value3",
839
},
840
],
841
];
842
const expected: SitePage[] = [
843
{
844
id: "123",
845
title: "fake-title",
846
metadata: {
847
["fake-key"]: "fake-value",
848
},
849
ancestors: [{ id: "fake-ancestor" }],
850
},
851
{
852
id: "456",
853
title: "fake-title2",
854
metadata: {
855
["fake-key2"]: "fake-value2",
856
["fake-key3"]: "fake-value3",
857
},
858
ancestors: [{ id: "fake-ancestor-2" }],
859
},
860
];
861
const result = mergeSitePages(shallowPages, contentProperties);
862
assertEquals(expected, result);
863
});
864
865
test(suiteLabel("not_matching"), async () => {
866
const shallowPages: ContentSummary[] = [
867
{
868
id: "123",
869
title: "fake-title",
870
ancestors: [{ id: "fake-ancestor" }],
871
},
872
{
873
id: "456",
874
title: "fake-title2",
875
ancestors: [{ id: "fake-ancestor-2" }],
876
},
877
];
878
const contentProperties = [
879
[
880
{
881
key: "fake-key",
882
value: "fake-value",
883
},
884
],
885
];
886
const expected: SitePage[] = [
887
{
888
id: "123",
889
title: "fake-title",
890
metadata: {
891
["fake-key"]: "fake-value",
892
},
893
ancestors: [{ id: "fake-ancestor" }],
894
},
895
{
896
id: "456",
897
title: "fake-title2",
898
metadata: {},
899
ancestors: [{ id: "fake-ancestor-2" }],
900
},
901
];
902
const result = mergeSitePages(shallowPages, contentProperties);
903
assertEquals(expected, result);
904
});
905
};
906
907
const runBuildSpaceChanges = () => {
908
const suiteLabel = (label: string) => `BuildSpaceChanges_${label}`;
909
910
const fakeSpace: Space = {
911
key: "fake-space-key",
912
id: "fake-space-id",
913
homepage: buildFakeContent(),
914
};
915
916
const fakeFile: SiteFileMetadata = {
917
fileName: "fake-file-name",
918
title: "fake-title",
919
originalTitle: "fake-title-original",
920
contentBody: {
921
storage: {
922
value: "fake-value",
923
representation: "storage",
924
},
925
},
926
};
927
928
const fakeFileMatchingPage: SiteFileMetadata = {
929
fileName: "fake-file-name",
930
title: "fake-title",
931
originalTitle: "fake-title-original",
932
contentBody: {
933
storage: {
934
value: "fake-value",
935
representation: "storage",
936
},
937
},
938
};
939
940
const fakeFile2: SiteFileMetadata = {
941
fileName: "fake-file-name2",
942
title: "fake-title2",
943
originalTitle: "fake-title2-original",
944
contentBody: {
945
storage: {
946
value: "fake-value2",
947
representation: "storage",
948
},
949
},
950
};
951
952
test(suiteLabel("no_files"), async () => {
953
const fileMetadataList: SiteFileMetadata[] = [];
954
const expected: ConfluenceSpaceChange[] = [];
955
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
956
fileMetadataList,
957
FAKE_PARENT,
958
fakeSpace
959
);
960
assertEquals(expected, actual);
961
});
962
963
test(suiteLabel("one_file"), async () => {
964
const fileMetadataList: SiteFileMetadata[] = [fakeFile];
965
const expected: ConfluenceSpaceChange[] = [
966
{
967
contentChangeType: ContentChangeType.create,
968
ancestors: [
969
{
970
id: "8781825",
971
},
972
],
973
body: {
974
storage: {
975
representation: "storage",
976
value: "fake-value",
977
},
978
},
979
fileName: "fake-file-name",
980
space: {
981
key: "fake-space-key",
982
id: "fake-space-id",
983
homepage: buildFakeContent(),
984
},
985
status: "current",
986
title: "fake-title",
987
type: "page",
988
},
989
];
990
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
991
fileMetadataList,
992
FAKE_PARENT,
993
fakeSpace
994
);
995
assertEquals(expected, actual);
996
});
997
998
test(suiteLabel("two_files"), async () => {
999
const fileMetadataList: SiteFileMetadata[] = [fakeFile, fakeFile2];
1000
const expected: ConfluenceSpaceChange[] = [
1001
{
1002
contentChangeType: ContentChangeType.create,
1003
ancestors: [
1004
{
1005
id: "8781825",
1006
},
1007
],
1008
body: {
1009
storage: {
1010
representation: "storage",
1011
value: "fake-value",
1012
},
1013
},
1014
fileName: "fake-file-name",
1015
space: {
1016
key: "fake-space-key",
1017
id: "fake-space-id",
1018
homepage: buildFakeContent(),
1019
},
1020
status: "current",
1021
title: "fake-title",
1022
type: "page",
1023
},
1024
{
1025
contentChangeType: ContentChangeType.create,
1026
ancestors: [
1027
{
1028
id: "8781825",
1029
},
1030
],
1031
body: {
1032
storage: {
1033
representation: "storage",
1034
value: "fake-value2",
1035
},
1036
},
1037
fileName: "fake-file-name2",
1038
space: {
1039
key: "fake-space-key",
1040
id: "fake-space-id",
1041
homepage: buildFakeContent(),
1042
},
1043
status: "current",
1044
title: "fake-title2",
1045
type: "page",
1046
},
1047
];
1048
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1049
fileMetadataList,
1050
FAKE_PARENT,
1051
fakeSpace
1052
);
1053
assertEquals(expected, actual);
1054
});
1055
1056
test(suiteLabel("one_file_update"), async () => {
1057
const fileMetadataList: SiteFileMetadata[] = [fakeFile];
1058
const expected: ConfluenceSpaceChange[] = [
1059
{
1060
contentChangeType: ContentChangeType.update,
1061
ancestors: [
1062
{
1063
id: "8781825",
1064
},
1065
],
1066
body: {
1067
storage: {
1068
representation: "storage",
1069
value: "fake-value",
1070
},
1071
},
1072
fileName: "fake-file-name",
1073
status: "current",
1074
title: "fake-title",
1075
type: "page",
1076
id: "123456",
1077
version: null,
1078
},
1079
];
1080
const existingSite: SitePage[] = [
1081
{
1082
id: "123456",
1083
title: "fake-title",
1084
metadata: { fileName: "fake-file-name" },
1085
},
1086
];
1087
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1088
fileMetadataList,
1089
FAKE_PARENT,
1090
fakeSpace,
1091
existingSite
1092
);
1093
assertEquals(expected, actual);
1094
});
1095
1096
test(suiteLabel("one_file_update_matching"), async () => {
1097
const fileMetadataList: SiteFileMetadata[] = [fakeFileMatchingPage];
1098
const expected: ConfluenceSpaceChange[] = [
1099
{
1100
contentChangeType: ContentChangeType.update,
1101
ancestors: [
1102
{
1103
id: "8781825",
1104
},
1105
],
1106
body: {
1107
storage: {
1108
representation: "storage",
1109
value: "fake-value",
1110
},
1111
},
1112
fileName: "fake-file-name",
1113
status: "current",
1114
title: "fake-title",
1115
type: "page",
1116
id: "123456",
1117
version: null,
1118
},
1119
];
1120
const existingSite: SitePage[] = [
1121
{
1122
id: "123456",
1123
title: "fake-title",
1124
metadata: { fileName: "fake-file-name" },
1125
},
1126
];
1127
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1128
fileMetadataList,
1129
FAKE_PARENT,
1130
fakeSpace,
1131
existingSite
1132
);
1133
assertEquals(expected, actual);
1134
});
1135
1136
test(suiteLabel("findPagesToDelete"), async () => {
1137
const fileMetadataList: SiteFileMetadata[] = [fakeFile];
1138
const existingSite: SitePage[] = [
1139
{
1140
id: "fake-file-id",
1141
title: "fake-title",
1142
metadata: { fileName: "fake-file-name" },
1143
ancestors: [],
1144
},
1145
{
1146
id: "delete-me-file-id",
1147
title: "delete-me-title",
1148
metadata: { fileName: "delete-me-file-name" },
1149
ancestors: [],
1150
},
1151
];
1152
const expected = [
1153
{
1154
id: "delete-me-file-id",
1155
title: "delete-me-title",
1156
metadata: { fileName: "delete-me-file-name" },
1157
ancestors: [],
1158
},
1159
];
1160
const actual = findPagesToDelete(fileMetadataList, existingSite);
1161
assertEquals(expected, actual);
1162
});
1163
1164
test(suiteLabel("one_file_delete"), async () => {
1165
const fileMetadataList: SiteFileMetadata[] = [fakeFile];
1166
const existingSite: SitePage[] = [
1167
{
1168
id: "fake-file-id",
1169
title: "fake-title",
1170
metadata: { fileName: "fake-file-name" },
1171
},
1172
{
1173
id: "delete-me-file-id",
1174
title: "delete-me-title",
1175
metadata: { fileName: "delete-me-file-name" },
1176
},
1177
];
1178
const expected: ConfluenceSpaceChange[] = [
1179
{
1180
id: "delete-me-file-id",
1181
contentChangeType: ContentChangeType.delete,
1182
},
1183
{
1184
contentChangeType: ContentChangeType.update,
1185
ancestors: [
1186
{
1187
id: "8781825",
1188
},
1189
],
1190
body: {
1191
storage: {
1192
representation: "storage",
1193
value: "fake-value",
1194
},
1195
},
1196
fileName: "fake-file-name",
1197
status: "current",
1198
title: "fake-title",
1199
type: "page",
1200
id: "fake-file-id",
1201
version: null,
1202
},
1203
];
1204
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1205
fileMetadataList,
1206
FAKE_PARENT,
1207
fakeSpace,
1208
existingSite
1209
);
1210
assertEquals(expected, actual);
1211
});
1212
};
1213
1214
const runSpaceCreatesWithNesting = () => {
1215
const suiteLabel = (label: string) => `SpaceCreatesWithNesting_${label}`;
1216
1217
const fakeSpace: Space = {
1218
key: "fake-space-key",
1219
id: "fake-space-id",
1220
homepage: buildFakeContent(),
1221
};
1222
1223
const fakeFile: SiteFileMetadata = {
1224
fileName: "fake-file-name",
1225
title: "fake-title",
1226
originalTitle: "fake-title-original",
1227
contentBody: {
1228
storage: {
1229
value: "fake-value",
1230
representation: "storage",
1231
},
1232
},
1233
};
1234
1235
const fakeNestedFile: SiteFileMetadata = {
1236
fileName: "fake-parent/fake-file-name",
1237
title: "fake-title",
1238
originalTitle: "fake-title-original",
1239
1240
contentBody: {
1241
storage: {
1242
value: "fake-value",
1243
representation: "storage",
1244
},
1245
},
1246
};
1247
1248
const fakeNestedFileWin: SiteFileMetadata = {
1249
fileName: "fake-parent\\fake-file-name",
1250
title: "fake-title",
1251
originalTitle: "fake-title-original",
1252
1253
contentBody: {
1254
storage: {
1255
value: "fake-value",
1256
representation: "storage",
1257
},
1258
},
1259
};
1260
1261
const fakeNestedFile2: SiteFileMetadata = {
1262
fileName: "fake-parent/fake-file-name2",
1263
title: "fake-title2",
1264
originalTitle: "fake-title2-original",
1265
1266
contentBody: {
1267
storage: {
1268
value: "fake-value",
1269
representation: "storage",
1270
},
1271
},
1272
};
1273
1274
const fakeNestedFile2Win: SiteFileMetadata = {
1275
fileName: "fake-parent\\fake-file-name2",
1276
title: "fake-title2",
1277
originalTitle: "fake-title2-original",
1278
1279
contentBody: {
1280
storage: {
1281
value: "fake-value",
1282
representation: "storage",
1283
},
1284
},
1285
};
1286
1287
const fakeNestedFile3: SiteFileMetadata = {
1288
fileName: "fake-parent2/fake-file-name3",
1289
title: "fake-title3",
1290
originalTitle: "fake-title3-original",
1291
1292
contentBody: {
1293
storage: {
1294
value: "fake-value",
1295
representation: "storage",
1296
},
1297
},
1298
};
1299
1300
const fakeNestedFile3Win: SiteFileMetadata = {
1301
fileName: "fake-parent2\\fake-file-name3",
1302
title: "fake-title3",
1303
originalTitle: "fake-title3-original",
1304
1305
contentBody: {
1306
storage: {
1307
value: "fake-value",
1308
representation: "storage",
1309
},
1310
},
1311
};
1312
1313
const fakeMultiNestedFile: SiteFileMetadata = {
1314
fileName:
1315
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name",
1316
title: "fake-title",
1317
originalTitle: "fake-title-original",
1318
1319
contentBody: {
1320
storage: {
1321
value: "fake-value",
1322
representation: "storage",
1323
},
1324
},
1325
};
1326
1327
const fakeMultiNestedFileWin: SiteFileMetadata = {
1328
fileName:
1329
"fake-great-grand-parent\\fake-grand-parent\\fake-parent\\fake-file-name",
1330
title: "fake-title",
1331
originalTitle: "fake-title-original",
1332
1333
contentBody: {
1334
storage: {
1335
value: "fake-value",
1336
representation: "storage",
1337
},
1338
},
1339
};
1340
1341
test(suiteLabel("one_nested_file"), async () => {
1342
const fileMetadataList: SiteFileMetadata[] = [fakeNestedFile];
1343
const expected: ConfluenceSpaceChange[] = [
1344
{
1345
contentChangeType: ContentChangeType.create,
1346
ancestors: [
1347
{
1348
id: "8781825",
1349
},
1350
],
1351
body: {
1352
storage: {
1353
representation: "storage",
1354
value: "",
1355
},
1356
},
1357
fileName: "fake-parent",
1358
space: {
1359
key: "fake-space-key",
1360
id: "fake-space-id",
1361
homepage: buildFakeContent(),
1362
},
1363
status: "current",
1364
title: "Fake-parent",
1365
type: "page",
1366
},
1367
{
1368
contentChangeType: ContentChangeType.create,
1369
ancestors: [
1370
{
1371
id: "fake-parent",
1372
},
1373
],
1374
body: {
1375
storage: {
1376
representation: "storage",
1377
value: "fake-value",
1378
},
1379
},
1380
fileName: "fake-parent/fake-file-name",
1381
space: {
1382
key: "fake-space-key",
1383
id: "fake-space-id",
1384
homepage: buildFakeContent(),
1385
},
1386
status: "current",
1387
title: "fake-title",
1388
type: "page",
1389
},
1390
];
1391
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1392
fileMetadataList,
1393
FAKE_PARENT,
1394
fakeSpace
1395
);
1396
assertEquals(expected, actual);
1397
});
1398
1399
test(suiteLabel("one_nested_file_win"), async () => {
1400
const fileMetadataList: SiteFileMetadata[] = [fakeNestedFileWin];
1401
const expected: ConfluenceSpaceChange[] = [
1402
{
1403
contentChangeType: ContentChangeType.create,
1404
ancestors: [
1405
{
1406
id: "8781825",
1407
},
1408
],
1409
body: {
1410
storage: {
1411
representation: "storage",
1412
value: "",
1413
},
1414
},
1415
fileName: "fake-parent",
1416
space: {
1417
key: "fake-space-key",
1418
id: "fake-space-id",
1419
homepage: buildFakeContent(),
1420
},
1421
status: "current",
1422
title: "Fake-parent",
1423
type: "page",
1424
},
1425
{
1426
contentChangeType: ContentChangeType.create,
1427
ancestors: [
1428
{
1429
id: "fake-parent",
1430
},
1431
],
1432
body: {
1433
storage: {
1434
representation: "storage",
1435
value: "fake-value",
1436
},
1437
},
1438
fileName: "fake-parent/fake-file-name",
1439
space: {
1440
key: "fake-space-key",
1441
id: "fake-space-id",
1442
homepage: buildFakeContent(),
1443
},
1444
status: "current",
1445
title: "fake-title",
1446
type: "page",
1447
},
1448
];
1449
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1450
fileMetadataList,
1451
FAKE_PARENT,
1452
fakeSpace
1453
);
1454
assertEquals(expected, actual);
1455
});
1456
1457
test(suiteLabel("one_nested_file_add_back_empty_parent"), async () => {
1458
const fileMetadataList: SiteFileMetadata[] = [fakeNestedFile];
1459
const existingSite: SitePage[] = [
1460
{
1461
id: "fake-parent-id",
1462
title: "Fake-parent",
1463
metadata: { fileName: "fake-parent" },
1464
},
1465
];
1466
const expected: ConfluenceSpaceChange[] = [
1467
{
1468
contentChangeType: ContentChangeType.create,
1469
ancestors: [
1470
{
1471
id: "fake-parent-id",
1472
},
1473
],
1474
body: {
1475
storage: {
1476
representation: "storage",
1477
value: "fake-value",
1478
},
1479
},
1480
fileName: "fake-parent/fake-file-name",
1481
space: {
1482
key: "fake-space-key",
1483
id: "fake-space-id",
1484
homepage: buildFakeContent(),
1485
},
1486
status: "current",
1487
title: "fake-title",
1488
type: "page",
1489
},
1490
];
1491
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1492
fileMetadataList,
1493
FAKE_PARENT,
1494
fakeSpace,
1495
existingSite
1496
);
1497
assertEquals(expected, actual);
1498
});
1499
1500
test(suiteLabel("one_multi_nested_file"), async () => {
1501
const fileMetadataList: SiteFileMetadata[] = [fakeMultiNestedFile];
1502
const expected: ConfluenceSpaceChange[] = [
1503
{
1504
contentChangeType: ContentChangeType.create,
1505
ancestors: [
1506
{
1507
id: "8781825",
1508
},
1509
],
1510
body: {
1511
storage: {
1512
representation: "storage",
1513
value: "",
1514
},
1515
},
1516
fileName: "fake-great-grand-parent",
1517
space: {
1518
key: "fake-space-key",
1519
id: "fake-space-id",
1520
homepage: buildFakeContent(),
1521
},
1522
status: "current",
1523
title: "Fake-great-grand-parent",
1524
type: "page",
1525
},
1526
{
1527
contentChangeType: ContentChangeType.create,
1528
ancestors: [
1529
{
1530
id: "fake-great-grand-parent",
1531
},
1532
],
1533
body: {
1534
storage: {
1535
representation: "storage",
1536
value: "",
1537
},
1538
},
1539
fileName: "fake-great-grand-parent/fake-grand-parent",
1540
space: {
1541
key: "fake-space-key",
1542
id: "fake-space-id",
1543
homepage: buildFakeContent(),
1544
},
1545
status: "current",
1546
title: "Fake-grand-parent",
1547
type: "page",
1548
},
1549
{
1550
contentChangeType: ContentChangeType.create,
1551
ancestors: [
1552
{
1553
id: "fake-great-grand-parent/fake-grand-parent",
1554
},
1555
],
1556
body: {
1557
storage: {
1558
representation: "storage",
1559
value: "",
1560
},
1561
},
1562
fileName: "fake-great-grand-parent/fake-grand-parent/fake-parent",
1563
space: {
1564
key: "fake-space-key",
1565
id: "fake-space-id",
1566
homepage: buildFakeContent(),
1567
},
1568
status: "current",
1569
title: "Fake-parent",
1570
type: "page",
1571
},
1572
{
1573
contentChangeType: ContentChangeType.create,
1574
ancestors: [
1575
{
1576
id: "fake-great-grand-parent/fake-grand-parent/fake-parent",
1577
},
1578
],
1579
body: {
1580
storage: {
1581
representation: "storage",
1582
value: "fake-value",
1583
},
1584
},
1585
fileName:
1586
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name",
1587
space: {
1588
key: "fake-space-key",
1589
id: "fake-space-id",
1590
homepage: buildFakeContent(),
1591
},
1592
status: "current",
1593
title: "fake-title",
1594
type: "page",
1595
},
1596
];
1597
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1598
fileMetadataList,
1599
FAKE_PARENT,
1600
fakeSpace
1601
);
1602
1603
assertEquals(expected, actual);
1604
});
1605
1606
test(suiteLabel("one_multi_nested_file_win"), async () => {
1607
const fileMetadataList: SiteFileMetadata[] = [fakeMultiNestedFileWin];
1608
const expected: ConfluenceSpaceChange[] = [
1609
{
1610
contentChangeType: ContentChangeType.create,
1611
ancestors: [
1612
{
1613
id: "8781825",
1614
},
1615
],
1616
body: {
1617
storage: {
1618
representation: "storage",
1619
value: "",
1620
},
1621
},
1622
fileName: "fake-great-grand-parent",
1623
space: {
1624
key: "fake-space-key",
1625
id: "fake-space-id",
1626
homepage: buildFakeContent(),
1627
},
1628
status: "current",
1629
title: "Fake-great-grand-parent",
1630
type: "page",
1631
},
1632
{
1633
contentChangeType: ContentChangeType.create,
1634
ancestors: [
1635
{
1636
id: "fake-great-grand-parent",
1637
},
1638
],
1639
body: {
1640
storage: {
1641
representation: "storage",
1642
value: "",
1643
},
1644
},
1645
fileName: "fake-great-grand-parent/fake-grand-parent",
1646
space: {
1647
key: "fake-space-key",
1648
id: "fake-space-id",
1649
homepage: buildFakeContent(),
1650
},
1651
status: "current",
1652
title: "Fake-grand-parent",
1653
type: "page",
1654
},
1655
{
1656
contentChangeType: ContentChangeType.create,
1657
ancestors: [
1658
{
1659
id: "fake-great-grand-parent/fake-grand-parent",
1660
},
1661
],
1662
body: {
1663
storage: {
1664
representation: "storage",
1665
value: "",
1666
},
1667
},
1668
fileName: "fake-great-grand-parent/fake-grand-parent/fake-parent",
1669
space: {
1670
key: "fake-space-key",
1671
id: "fake-space-id",
1672
homepage: buildFakeContent(),
1673
},
1674
status: "current",
1675
title: "Fake-parent",
1676
type: "page",
1677
},
1678
{
1679
contentChangeType: ContentChangeType.create,
1680
ancestors: [
1681
{
1682
id: "fake-great-grand-parent/fake-grand-parent/fake-parent",
1683
},
1684
],
1685
body: {
1686
storage: {
1687
representation: "storage",
1688
value: "fake-value",
1689
},
1690
},
1691
fileName:
1692
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name",
1693
space: {
1694
key: "fake-space-key",
1695
id: "fake-space-id",
1696
homepage: buildFakeContent(),
1697
},
1698
status: "current",
1699
title: "fake-title",
1700
type: "page",
1701
},
1702
];
1703
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1704
fileMetadataList,
1705
FAKE_PARENT,
1706
fakeSpace
1707
);
1708
1709
assertEquals(expected, actual);
1710
});
1711
1712
test(suiteLabel("two_nested_files_same_parent"), async () => {
1713
const fileMetadataList: SiteFileMetadata[] = [
1714
fakeNestedFile,
1715
fakeNestedFile2,
1716
];
1717
const expected: ConfluenceSpaceChange[] = [
1718
{
1719
contentChangeType: ContentChangeType.create,
1720
ancestors: [
1721
{
1722
id: "8781825",
1723
},
1724
],
1725
body: {
1726
storage: {
1727
representation: "storage",
1728
value: "",
1729
},
1730
},
1731
fileName: "fake-parent",
1732
space: {
1733
key: "fake-space-key",
1734
id: "fake-space-id",
1735
homepage: buildFakeContent(),
1736
},
1737
status: "current",
1738
title: "Fake-parent",
1739
type: "page",
1740
},
1741
{
1742
contentChangeType: ContentChangeType.create,
1743
ancestors: [
1744
{
1745
id: "fake-parent",
1746
},
1747
],
1748
body: {
1749
storage: {
1750
representation: "storage",
1751
value: "fake-value",
1752
},
1753
},
1754
fileName: "fake-parent/fake-file-name",
1755
space: {
1756
key: "fake-space-key",
1757
id: "fake-space-id",
1758
homepage: buildFakeContent(),
1759
},
1760
status: "current",
1761
title: "fake-title",
1762
type: "page",
1763
},
1764
{
1765
contentChangeType: ContentChangeType.create,
1766
ancestors: [
1767
{
1768
id: "fake-parent",
1769
},
1770
],
1771
body: {
1772
storage: {
1773
representation: "storage",
1774
value: "fake-value",
1775
},
1776
},
1777
fileName: "fake-parent/fake-file-name2",
1778
space: {
1779
key: "fake-space-key",
1780
id: "fake-space-id",
1781
homepage: buildFakeContent(),
1782
},
1783
status: "current",
1784
title: "fake-title2",
1785
type: "page",
1786
},
1787
];
1788
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1789
fileMetadataList,
1790
FAKE_PARENT,
1791
fakeSpace
1792
);
1793
assertEquals(expected, actual);
1794
});
1795
1796
test(suiteLabel("two_nested_files_different_parent"), async () => {
1797
const fileMetadataList: SiteFileMetadata[] = [
1798
fakeNestedFile,
1799
fakeNestedFile3,
1800
];
1801
const expected: ConfluenceSpaceChange[] = [
1802
{
1803
contentChangeType: ContentChangeType.create,
1804
ancestors: [
1805
{
1806
id: "8781825",
1807
},
1808
],
1809
body: {
1810
storage: {
1811
representation: "storage",
1812
value: "",
1813
},
1814
},
1815
fileName: "fake-parent",
1816
space: {
1817
key: "fake-space-key",
1818
id: "fake-space-id",
1819
homepage: buildFakeContent(),
1820
},
1821
status: "current",
1822
title: "Fake-parent",
1823
type: "page",
1824
},
1825
{
1826
contentChangeType: ContentChangeType.create,
1827
ancestors: [
1828
{
1829
id: "fake-parent",
1830
},
1831
],
1832
body: {
1833
storage: {
1834
representation: "storage",
1835
value: "fake-value",
1836
},
1837
},
1838
fileName: "fake-parent/fake-file-name",
1839
space: {
1840
key: "fake-space-key",
1841
id: "fake-space-id",
1842
homepage: buildFakeContent(),
1843
},
1844
status: "current",
1845
title: "fake-title",
1846
type: "page",
1847
},
1848
{
1849
contentChangeType: ContentChangeType.create,
1850
ancestors: [
1851
{
1852
id: "8781825",
1853
},
1854
],
1855
body: {
1856
storage: {
1857
representation: "storage",
1858
value: "",
1859
},
1860
},
1861
fileName: "fake-parent2",
1862
space: {
1863
key: "fake-space-key",
1864
id: "fake-space-id",
1865
homepage: buildFakeContent(),
1866
},
1867
status: "current",
1868
title: "Fake-parent2",
1869
type: "page",
1870
},
1871
{
1872
contentChangeType: ContentChangeType.create,
1873
ancestors: [
1874
{
1875
id: "fake-parent2",
1876
},
1877
],
1878
body: {
1879
storage: {
1880
representation: "storage",
1881
value: "fake-value",
1882
},
1883
},
1884
fileName: "fake-parent2/fake-file-name3",
1885
space: {
1886
key: "fake-space-key",
1887
id: "fake-space-id",
1888
homepage: buildFakeContent(),
1889
},
1890
status: "current",
1891
title: "fake-title3",
1892
type: "page",
1893
},
1894
];
1895
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
1896
fileMetadataList,
1897
FAKE_PARENT,
1898
fakeSpace
1899
);
1900
assertEquals(expected, actual);
1901
});
1902
1903
test(suiteLabel("two_nested_files_different_parent_win"), async () => {
1904
const fileMetadataList: SiteFileMetadata[] = [
1905
fakeNestedFileWin,
1906
fakeNestedFile3Win,
1907
];
1908
const expected: ConfluenceSpaceChange[] = [
1909
{
1910
contentChangeType: ContentChangeType.create,
1911
ancestors: [
1912
{
1913
id: "8781825",
1914
},
1915
],
1916
body: {
1917
storage: {
1918
representation: "storage",
1919
value: "",
1920
},
1921
},
1922
fileName: "fake-parent",
1923
space: {
1924
key: "fake-space-key",
1925
id: "fake-space-id",
1926
homepage: buildFakeContent(),
1927
},
1928
status: "current",
1929
title: "Fake-parent",
1930
type: "page",
1931
},
1932
{
1933
contentChangeType: ContentChangeType.create,
1934
ancestors: [
1935
{
1936
id: "fake-parent",
1937
},
1938
],
1939
body: {
1940
storage: {
1941
representation: "storage",
1942
value: "fake-value",
1943
},
1944
},
1945
fileName: "fake-parent/fake-file-name",
1946
space: {
1947
key: "fake-space-key",
1948
id: "fake-space-id",
1949
homepage: buildFakeContent(),
1950
},
1951
status: "current",
1952
title: "fake-title",
1953
type: "page",
1954
},
1955
{
1956
contentChangeType: ContentChangeType.create,
1957
ancestors: [
1958
{
1959
id: "8781825",
1960
},
1961
],
1962
body: {
1963
storage: {
1964
representation: "storage",
1965
value: "",
1966
},
1967
},
1968
fileName: "fake-parent2",
1969
space: {
1970
key: "fake-space-key",
1971
id: "fake-space-id",
1972
homepage: buildFakeContent(),
1973
},
1974
status: "current",
1975
title: "Fake-parent2",
1976
type: "page",
1977
},
1978
{
1979
contentChangeType: ContentChangeType.create,
1980
ancestors: [
1981
{
1982
id: "fake-parent2",
1983
},
1984
],
1985
body: {
1986
storage: {
1987
representation: "storage",
1988
value: "fake-value",
1989
},
1990
},
1991
fileName: "fake-parent2/fake-file-name3",
1992
space: {
1993
key: "fake-space-key",
1994
id: "fake-space-id",
1995
homepage: buildFakeContent(),
1996
},
1997
status: "current",
1998
title: "fake-title3",
1999
type: "page",
2000
},
2001
];
2002
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2003
fileMetadataList,
2004
FAKE_PARENT,
2005
fakeSpace
2006
);
2007
assertEquals(expected, actual);
2008
});
2009
2010
test(suiteLabel("three_nested_files_same_different_parent"), async () => {
2011
const fileMetadataList: SiteFileMetadata[] = [
2012
fakeNestedFile,
2013
fakeNestedFile2,
2014
fakeNestedFile3,
2015
];
2016
const expected: ConfluenceSpaceChange[] = [
2017
{
2018
contentChangeType: ContentChangeType.create,
2019
ancestors: [
2020
{
2021
id: "8781825",
2022
},
2023
],
2024
body: {
2025
storage: {
2026
representation: "storage",
2027
value: "",
2028
},
2029
},
2030
fileName: "fake-parent",
2031
space: {
2032
key: "fake-space-key",
2033
id: "fake-space-id",
2034
homepage: buildFakeContent(),
2035
},
2036
status: "current",
2037
title: "Fake-parent",
2038
type: "page",
2039
},
2040
{
2041
contentChangeType: ContentChangeType.create,
2042
ancestors: [
2043
{
2044
id: "fake-parent",
2045
},
2046
],
2047
body: {
2048
storage: {
2049
representation: "storage",
2050
value: "fake-value",
2051
},
2052
},
2053
fileName: "fake-parent/fake-file-name",
2054
space: {
2055
key: "fake-space-key",
2056
id: "fake-space-id",
2057
homepage: buildFakeContent(),
2058
},
2059
status: "current",
2060
title: "fake-title",
2061
type: "page",
2062
},
2063
{
2064
contentChangeType: ContentChangeType.create,
2065
ancestors: [
2066
{
2067
id: "fake-parent",
2068
},
2069
],
2070
body: {
2071
storage: {
2072
representation: "storage",
2073
value: "fake-value",
2074
},
2075
},
2076
fileName: "fake-parent/fake-file-name2",
2077
space: {
2078
key: "fake-space-key",
2079
id: "fake-space-id",
2080
homepage: buildFakeContent(),
2081
},
2082
status: "current",
2083
title: "fake-title2",
2084
type: "page",
2085
},
2086
{
2087
contentChangeType: ContentChangeType.create,
2088
ancestors: [
2089
{
2090
id: "8781825",
2091
},
2092
],
2093
body: {
2094
storage: {
2095
representation: "storage",
2096
value: "",
2097
},
2098
},
2099
fileName: "fake-parent2",
2100
space: {
2101
key: "fake-space-key",
2102
id: "fake-space-id",
2103
homepage: buildFakeContent(),
2104
},
2105
status: "current",
2106
title: "Fake-parent2",
2107
type: "page",
2108
},
2109
{
2110
contentChangeType: ContentChangeType.create,
2111
ancestors: [
2112
{
2113
id: "fake-parent2",
2114
},
2115
],
2116
body: {
2117
storage: {
2118
representation: "storage",
2119
value: "fake-value",
2120
},
2121
},
2122
fileName: "fake-parent2/fake-file-name3",
2123
space: {
2124
key: "fake-space-key",
2125
id: "fake-space-id",
2126
homepage: buildFakeContent(),
2127
},
2128
status: "current",
2129
title: "fake-title3",
2130
type: "page",
2131
},
2132
];
2133
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2134
fileMetadataList,
2135
FAKE_PARENT,
2136
fakeSpace
2137
);
2138
assertEquals(expected, actual);
2139
});
2140
2141
test(suiteLabel("three_nested_files_same_different_parent_win"), async () => {
2142
const fileMetadataList: SiteFileMetadata[] = [
2143
fakeNestedFileWin,
2144
fakeNestedFile2Win,
2145
fakeNestedFile3Win,
2146
];
2147
const expected: ConfluenceSpaceChange[] = [
2148
{
2149
contentChangeType: ContentChangeType.create,
2150
ancestors: [
2151
{
2152
id: "8781825",
2153
},
2154
],
2155
body: {
2156
storage: {
2157
representation: "storage",
2158
value: "",
2159
},
2160
},
2161
fileName: "fake-parent",
2162
space: {
2163
key: "fake-space-key",
2164
id: "fake-space-id",
2165
homepage: buildFakeContent(),
2166
},
2167
status: "current",
2168
title: "Fake-parent",
2169
type: "page",
2170
},
2171
{
2172
contentChangeType: ContentChangeType.create,
2173
ancestors: [
2174
{
2175
id: "fake-parent",
2176
},
2177
],
2178
body: {
2179
storage: {
2180
representation: "storage",
2181
value: "fake-value",
2182
},
2183
},
2184
fileName: "fake-parent/fake-file-name",
2185
space: {
2186
key: "fake-space-key",
2187
id: "fake-space-id",
2188
homepage: buildFakeContent(),
2189
},
2190
status: "current",
2191
title: "fake-title",
2192
type: "page",
2193
},
2194
{
2195
contentChangeType: ContentChangeType.create,
2196
ancestors: [
2197
{
2198
id: "fake-parent",
2199
},
2200
],
2201
body: {
2202
storage: {
2203
representation: "storage",
2204
value: "fake-value",
2205
},
2206
},
2207
fileName: "fake-parent/fake-file-name2",
2208
space: {
2209
key: "fake-space-key",
2210
id: "fake-space-id",
2211
homepage: buildFakeContent(),
2212
},
2213
status: "current",
2214
title: "fake-title2",
2215
type: "page",
2216
},
2217
{
2218
contentChangeType: ContentChangeType.create,
2219
ancestors: [
2220
{
2221
id: "8781825",
2222
},
2223
],
2224
body: {
2225
storage: {
2226
representation: "storage",
2227
value: "",
2228
},
2229
},
2230
fileName: "fake-parent2",
2231
space: {
2232
key: "fake-space-key",
2233
id: "fake-space-id",
2234
homepage: buildFakeContent(),
2235
},
2236
status: "current",
2237
title: "Fake-parent2",
2238
type: "page",
2239
},
2240
{
2241
contentChangeType: ContentChangeType.create,
2242
ancestors: [
2243
{
2244
id: "fake-parent2",
2245
},
2246
],
2247
body: {
2248
storage: {
2249
representation: "storage",
2250
value: "fake-value",
2251
},
2252
},
2253
fileName: "fake-parent2/fake-file-name3",
2254
space: {
2255
key: "fake-space-key",
2256
id: "fake-space-id",
2257
homepage: buildFakeContent(),
2258
},
2259
status: "current",
2260
title: "fake-title3",
2261
type: "page",
2262
},
2263
];
2264
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2265
fileMetadataList,
2266
FAKE_PARENT,
2267
fakeSpace
2268
);
2269
assertEquals(expected, actual);
2270
});
2271
};
2272
2273
const runSpaceUpdatesWithNesting = () => {
2274
const suiteLabel = (label: string) => `SpaceUpdatesWithNesting_${label}`;
2275
2276
const fakeSpace: Space = {
2277
key: "fake-space-key",
2278
id: "fake-space-id",
2279
homepage: buildFakeContent(),
2280
};
2281
2282
const fakeNestedFile: SiteFileMetadata = {
2283
fileName: "fake-parent/fake-file-name.xml",
2284
title: "fake-title",
2285
originalTitle: "fake-title-original",
2286
2287
contentBody: {
2288
storage: {
2289
value: "fake-value",
2290
representation: "storage",
2291
},
2292
},
2293
};
2294
2295
const fakeNestedFile2: SiteFileMetadata = {
2296
fileName: "fake-parent/fake-file-name2.xml",
2297
title: "fake-title2",
2298
originalTitle: "fake-title2-original",
2299
2300
contentBody: {
2301
storage: {
2302
value: "fake-value",
2303
representation: "storage",
2304
},
2305
},
2306
};
2307
2308
const fakeNestedFile3: SiteFileMetadata = {
2309
fileName: "fake-parent2/fake-file-name3.xml",
2310
title: "fake-title3",
2311
originalTitle: "fake-title3-original",
2312
2313
contentBody: {
2314
storage: {
2315
value: "fake-value",
2316
representation: "storage",
2317
},
2318
},
2319
};
2320
2321
const fakeMultiNestedFile: SiteFileMetadata = {
2322
fileName:
2323
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name.xml",
2324
title: "fake-title",
2325
originalTitle: "fake-title-original",
2326
2327
contentBody: {
2328
storage: {
2329
value: "fake-value",
2330
representation: "storage",
2331
},
2332
},
2333
};
2334
2335
const fakeMultiNestedFileWin: SiteFileMetadata = {
2336
fileName:
2337
"fake-great-grand-parent\\fake-grand-parent\\fake-parent\\fake-file-name.xml",
2338
title: "fake-title",
2339
originalTitle: "fake-title-original",
2340
2341
contentBody: {
2342
storage: {
2343
value: "fake-value",
2344
representation: "storage",
2345
},
2346
},
2347
};
2348
2349
test(suiteLabel("one_nested_file_update"), async () => {
2350
const fileMetadataList: SiteFileMetadata[] = [fakeNestedFile];
2351
2352
const existingSite = [
2353
{
2354
id: "123456",
2355
title: "fake-title",
2356
metadata: { fileName: "fake-parent/fake-file-name.xml" },
2357
ancestors: [{ id: "fake-parent-id" }, { id: "fake-grand-parent-id" }],
2358
},
2359
{
2360
title: "Fake Parent",
2361
id: "fake-parent-id",
2362
metadata: { editor: "v2", fileName: "fake-parent" },
2363
ancestors: [{ id: "fake-grand-parent-id" }],
2364
},
2365
];
2366
2367
const expected: ConfluenceSpaceChange[] = [
2368
{
2369
contentChangeType: ContentChangeType.update,
2370
ancestors: [
2371
{
2372
id: "fake-parent-id",
2373
},
2374
],
2375
body: {
2376
storage: {
2377
representation: "storage",
2378
value: "fake-value",
2379
},
2380
},
2381
fileName: "fake-parent/fake-file-name.xml",
2382
status: "current",
2383
title: "fake-title",
2384
type: "page",
2385
id: "123456",
2386
version: null,
2387
},
2388
];
2389
2390
assertEquals(findPagesToDelete(fileMetadataList, existingSite), []);
2391
2392
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2393
fileMetadataList,
2394
FAKE_PARENT,
2395
fakeSpace,
2396
existingSite
2397
);
2398
2399
assertEquals(expected, actual);
2400
});
2401
2402
test(suiteLabel("one_multi-nested_file_update"), async () => {
2403
const fileMetadataList: SiteFileMetadata[] = [fakeMultiNestedFile];
2404
2405
const existingSite = [
2406
{
2407
id: "123456",
2408
title: "fake-title",
2409
metadata: {
2410
fileName:
2411
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name.xml",
2412
},
2413
ancestors: [
2414
{ id: "fake-parent-id" },
2415
{ id: "fake-grand-parent-id" },
2416
{ id: "fake-great-grand-parent-id" },
2417
],
2418
},
2419
{
2420
title: "Fake Parent",
2421
id: "fake-parent-id",
2422
metadata: {
2423
editor: "v2",
2424
fileName: "fake-great-grand-parent/fake-grand-parent/fake-parent",
2425
},
2426
ancestors: [
2427
{ id: "fake-grand-parent-id" },
2428
{ id: "fake-great-grand-parent-id" },
2429
],
2430
},
2431
{
2432
title: "Fake Grand Parent",
2433
id: "fake-grand-parent-id",
2434
metadata: {
2435
editor: "v2",
2436
fileName: "fake-great-grand-parent/fake-grand-parent",
2437
},
2438
ancestors: [{ id: "fake-great-grand-parent-id" }],
2439
},
2440
{
2441
title: "Fake Great Grand Parent",
2442
id: "fake-great-grand-parent-id",
2443
metadata: { editor: "v2", fileName: "fake-great-grand-parent" },
2444
ancestors: [{ id: "fake-great-grand-parent-id" }],
2445
},
2446
];
2447
2448
const expected: ConfluenceSpaceChange[] = [
2449
{
2450
contentChangeType: ContentChangeType.update,
2451
ancestors: [
2452
{
2453
id: "fake-parent-id",
2454
},
2455
],
2456
body: {
2457
storage: {
2458
representation: "storage",
2459
value: "fake-value",
2460
},
2461
},
2462
fileName:
2463
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name.xml",
2464
status: "current",
2465
title: "fake-title",
2466
type: "page",
2467
id: "123456",
2468
version: null,
2469
},
2470
];
2471
2472
const pagesToDelete = findPagesToDelete(fileMetadataList, existingSite);
2473
2474
assertEquals(pagesToDelete, []);
2475
2476
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2477
fileMetadataList,
2478
FAKE_PARENT,
2479
fakeSpace,
2480
existingSite
2481
);
2482
2483
assertEquals(expected, actual);
2484
});
2485
2486
test(suiteLabel("one_multi-nested_file_update_win"), async () => {
2487
const fileMetadataList: SiteFileMetadata[] = [fakeMultiNestedFileWin];
2488
2489
const existingSite = [
2490
{
2491
id: "123456",
2492
title: "fake-title",
2493
metadata: {
2494
fileName:
2495
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name.xml",
2496
},
2497
ancestors: [
2498
{ id: "fake-parent-id" },
2499
{ id: "fake-grand-parent-id" },
2500
{ id: "fake-great-grand-parent-id" },
2501
],
2502
},
2503
{
2504
title: "Fake Parent",
2505
id: "fake-parent-id",
2506
metadata: {
2507
editor: "v2",
2508
fileName: "fake-great-grand-parent/fake-grand-parent/fake-parent",
2509
},
2510
ancestors: [
2511
{ id: "fake-grand-parent-id" },
2512
{ id: "fake-great-grand-parent-id" },
2513
],
2514
},
2515
{
2516
title: "Fake Grand Parent",
2517
id: "fake-grand-parent-id",
2518
metadata: {
2519
editor: "v2",
2520
fileName: "fake-great-grand-parent/fake-grand-parent",
2521
},
2522
ancestors: [{ id: "fake-great-grand-parent-id" }],
2523
},
2524
{
2525
title: "Fake Great Grand Parent",
2526
id: "fake-great-grand-parent-id",
2527
metadata: { editor: "v2", fileName: "fake-great-grand-parent" },
2528
ancestors: [{ id: "fake-great-grand-parent-id" }],
2529
},
2530
];
2531
2532
const expected: ConfluenceSpaceChange[] = [
2533
{
2534
contentChangeType: ContentChangeType.update,
2535
ancestors: [
2536
{
2537
id: "fake-parent-id",
2538
},
2539
],
2540
body: {
2541
storage: {
2542
representation: "storage",
2543
value: "fake-value",
2544
},
2545
},
2546
fileName:
2547
"fake-great-grand-parent/fake-grand-parent/fake-parent/fake-file-name.xml",
2548
status: "current",
2549
title: "fake-title",
2550
type: "page",
2551
id: "123456",
2552
version: null,
2553
},
2554
];
2555
2556
const pagesToDelete = findPagesToDelete(fileMetadataList, existingSite);
2557
2558
assertEquals(pagesToDelete, []);
2559
2560
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2561
fileMetadataList,
2562
FAKE_PARENT,
2563
fakeSpace,
2564
existingSite
2565
);
2566
2567
assertEquals(expected, actual);
2568
});
2569
2570
test(suiteLabel("two_nested_files_same_parent"), async () => {
2571
const fileMetadataList: SiteFileMetadata[] = [
2572
fakeNestedFile,
2573
fakeNestedFile2,
2574
];
2575
const existingSite = [
2576
{
2577
id: "fake-title-id",
2578
title: "fake-title",
2579
metadata: { fileName: "fake-parent/fake-file-name.xml" },
2580
ancestors: [{ id: "fake-parent-id" }, { id: "fake-grand-parent-id" }],
2581
},
2582
{
2583
id: "fake-title2-id",
2584
title: "fake-title2",
2585
metadata: { fileName: "fake-parent/fake-file-name2.xml" },
2586
ancestors: [{ id: "fake-parent-id" }, { id: "fake-grand-parent-id" }],
2587
},
2588
{
2589
title: "Fake Parent",
2590
id: "fake-parent-id",
2591
metadata: { editor: "v2", fileName: "fake-parent" },
2592
ancestors: [{ id: "fake-grand-parent-id" }],
2593
},
2594
];
2595
2596
const expected: ConfluenceSpaceChange[] = [
2597
{
2598
contentChangeType: ContentChangeType.update,
2599
ancestors: [
2600
{
2601
id: "fake-parent-id",
2602
},
2603
],
2604
body: {
2605
storage: {
2606
representation: "storage",
2607
value: "fake-value",
2608
},
2609
},
2610
fileName: "fake-parent/fake-file-name.xml",
2611
status: "current",
2612
title: "fake-title",
2613
type: "page",
2614
id: "fake-title-id",
2615
version: null,
2616
},
2617
{
2618
contentChangeType: ContentChangeType.update,
2619
ancestors: [
2620
{
2621
id: "fake-parent-id",
2622
},
2623
],
2624
body: {
2625
storage: {
2626
representation: "storage",
2627
value: "fake-value",
2628
},
2629
},
2630
fileName: "fake-parent/fake-file-name2.xml",
2631
status: "current",
2632
title: "fake-title2",
2633
type: "page",
2634
id: "fake-title2-id",
2635
version: null,
2636
},
2637
];
2638
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2639
fileMetadataList,
2640
FAKE_PARENT,
2641
fakeSpace,
2642
existingSite
2643
);
2644
assertEquals(expected, actual);
2645
});
2646
2647
test(suiteLabel("three_nested_files_same_different_parent"), async () => {
2648
const fileMetadataList: SiteFileMetadata[] = [
2649
fakeNestedFile,
2650
fakeNestedFile2,
2651
fakeNestedFile3,
2652
];
2653
2654
const existingSite = [
2655
{
2656
id: "fake-title-id",
2657
title: "fake-title",
2658
metadata: { fileName: "fake-parent/fake-file-name.xml" },
2659
ancestors: [{ id: "fake-parent-id" }, { id: "fake-grand-parent-id" }],
2660
},
2661
{
2662
id: "fake-title2-id",
2663
title: "fake-title2",
2664
metadata: { fileName: "fake-parent/fake-file-name2.xml" },
2665
ancestors: [{ id: "fake-parent-id" }, { id: "fake-grand-parent-id" }],
2666
},
2667
{
2668
id: "fake-title3-id",
2669
title: "fake-title3",
2670
metadata: { fileName: "fake-parent2/fake-file-name3.xml" },
2671
ancestors: [{ id: "fake-parent2-id" }, { id: "fake-grand-parent-id" }],
2672
},
2673
{
2674
title: "Fake Parent",
2675
id: "fake-parent-id",
2676
metadata: { editor: "v2", fileName: "fake-parent" },
2677
ancestors: [{ id: "fake-grand-parent-id" }],
2678
},
2679
{
2680
title: "Fake Parent2",
2681
id: "fake-parent2-id",
2682
metadata: { editor: "v2", fileName: "fake-parent2" },
2683
ancestors: [{ id: "fake-grand-parent-id" }],
2684
},
2685
];
2686
2687
const expected: ConfluenceSpaceChange[] = [
2688
{
2689
contentChangeType: ContentChangeType.update,
2690
ancestors: [
2691
{
2692
id: "fake-parent-id",
2693
},
2694
],
2695
body: {
2696
storage: {
2697
representation: "storage",
2698
value: "fake-value",
2699
},
2700
},
2701
fileName: "fake-parent/fake-file-name.xml",
2702
status: "current",
2703
title: "fake-title",
2704
type: "page",
2705
id: "fake-title-id",
2706
version: null,
2707
},
2708
{
2709
contentChangeType: ContentChangeType.update,
2710
ancestors: [
2711
{
2712
id: "fake-parent-id",
2713
},
2714
],
2715
body: {
2716
storage: {
2717
representation: "storage",
2718
value: "fake-value",
2719
},
2720
},
2721
fileName: "fake-parent/fake-file-name2.xml",
2722
status: "current",
2723
title: "fake-title2",
2724
type: "page",
2725
id: "fake-title2-id",
2726
version: null,
2727
},
2728
{
2729
contentChangeType: ContentChangeType.update,
2730
ancestors: [
2731
{
2732
id: "fake-parent2-id",
2733
},
2734
],
2735
body: {
2736
storage: {
2737
representation: "storage",
2738
value: "fake-value",
2739
},
2740
},
2741
fileName: "fake-parent2/fake-file-name3.xml",
2742
status: "current",
2743
title: "fake-title3",
2744
type: "page",
2745
id: "fake-title3-id",
2746
version: null,
2747
},
2748
];
2749
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2750
fileMetadataList,
2751
FAKE_PARENT,
2752
fakeSpace,
2753
existingSite
2754
);
2755
assertEquals(expected, actual);
2756
});
2757
};
2758
2759
const runSpaceUpdatesWithNestedMoves = () => {
2760
const suiteLabel = (label: string) => `SpaceUpdatesWithNesting_${label}`;
2761
2762
const fakeSpace: Space = {
2763
key: "fake-space-key",
2764
id: "fake-space-id",
2765
homepage: buildFakeContent(),
2766
};
2767
2768
const fakeNestedFile: SiteFileMetadata = {
2769
fileName: "fake-parent/fake-file-name.xml",
2770
title: "fake-title",
2771
originalTitle: "fake-title-original",
2772
contentBody: {
2773
storage: {
2774
value: "fake-value",
2775
representation: "storage",
2776
},
2777
},
2778
};
2779
2780
const fakeRootFile: SiteFileMetadata = {
2781
fileName: "fake-file-name.xml",
2782
title: "fake-title",
2783
originalTitle: "fake-title-original",
2784
contentBody: {
2785
storage: {
2786
value: "fake-value",
2787
representation: "storage",
2788
},
2789
},
2790
};
2791
2792
const fakeNestedFile2: SiteFileMetadata = {
2793
fileName: "fake-parent/fake-file-name2.xml",
2794
title: "fake-title2",
2795
originalTitle: "fake-title2-original",
2796
2797
contentBody: {
2798
storage: {
2799
value: "fake-value",
2800
representation: "storage",
2801
},
2802
},
2803
};
2804
2805
const fakeNestedFile3: SiteFileMetadata = {
2806
fileName: "fake-parent2/fake-file-name3.xml",
2807
title: "fake-title3",
2808
originalTitle: "fake-title3-original",
2809
2810
contentBody: {
2811
storage: {
2812
value: "fake-value",
2813
representation: "storage",
2814
},
2815
},
2816
};
2817
2818
const fakeMultiNestedFile: SiteFileMetadata = {
2819
fileName: "fake-parent/fake-inner-parent/fake-file-name.xml",
2820
title: "fake-multi-nested-title",
2821
originalTitle: "fake-multi-nested-title-original",
2822
contentBody: {
2823
storage: {
2824
value: "fake-value",
2825
representation: "storage",
2826
},
2827
},
2828
};
2829
2830
test(suiteLabel("move_from_root_to_nested_parent"), async () => {
2831
const fileMetadataList: SiteFileMetadata[] = [fakeNestedFile];
2832
const existingSite = [
2833
{
2834
id: "fake-title-id",
2835
title: "fake-title",
2836
metadata: { fileName: "fake-file-name.xml" },
2837
ancestors: [{ id: "fake-grand-parent-id" }],
2838
},
2839
];
2840
2841
const expected: ConfluenceSpaceChange[] = [
2842
{
2843
contentChangeType: ContentChangeType.delete,
2844
id: "fake-title-id",
2845
},
2846
{
2847
contentChangeType: ContentChangeType.create,
2848
ancestors: [
2849
{
2850
id: "8781825",
2851
},
2852
],
2853
body: {
2854
storage: {
2855
representation: "storage",
2856
value: "",
2857
},
2858
},
2859
fileName: "fake-parent",
2860
space: {
2861
key: "fake-space-key",
2862
id: "fake-space-id",
2863
homepage: buildFakeContent(),
2864
},
2865
status: "current",
2866
title: "Fake-parent",
2867
type: "page",
2868
},
2869
{
2870
contentChangeType: ContentChangeType.create,
2871
ancestors: [
2872
{
2873
id: "fake-parent",
2874
},
2875
],
2876
body: {
2877
storage: {
2878
representation: "storage",
2879
value: "fake-value",
2880
},
2881
},
2882
fileName: "fake-parent/fake-file-name.xml",
2883
space: {
2884
key: "fake-space-key",
2885
id: "fake-space-id",
2886
homepage: buildFakeContent(),
2887
},
2888
status: "current",
2889
title: "fake-title",
2890
type: "page",
2891
},
2892
];
2893
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2894
fileMetadataList,
2895
FAKE_PARENT,
2896
fakeSpace,
2897
existingSite
2898
);
2899
assertEquals(expected, actual);
2900
});
2901
2902
test(suiteLabel("add_file_existing_parent"), async () => {
2903
const fileMetadataList: SiteFileMetadata[] = [
2904
fakeNestedFile,
2905
fakeNestedFile2,
2906
];
2907
const existingSite = [
2908
{
2909
id: "fake-title-id",
2910
title: "fake-title",
2911
metadata: { fileName: "fake-parent/fake-file-name.xml" },
2912
ancestors: [{ id: "fake-grand-parent-id" }, { id: "fake-parent-id" }],
2913
},
2914
{
2915
title: "Fake Parent",
2916
id: "fake-parent-id",
2917
metadata: { editor: "v2", fileName: "fake-parent" },
2918
ancestors: [{ id: "fake-grand-parent-id" }],
2919
},
2920
];
2921
2922
const expected: ConfluenceSpaceChange[] = [
2923
{
2924
contentChangeType: ContentChangeType.update,
2925
id: "fake-title-id",
2926
version: null,
2927
title: "fake-title",
2928
type: "page",
2929
status: "current",
2930
ancestors: [{ id: "fake-parent-id" }],
2931
body: { storage: { value: "fake-value", representation: "storage" } },
2932
fileName: "fake-parent/fake-file-name.xml",
2933
},
2934
{
2935
contentChangeType: ContentChangeType.create,
2936
ancestors: [
2937
{
2938
id: "fake-parent-id",
2939
},
2940
],
2941
body: {
2942
storage: {
2943
representation: "storage",
2944
value: "fake-value",
2945
},
2946
},
2947
fileName: "fake-parent/fake-file-name2.xml",
2948
space: {
2949
key: "fake-space-key",
2950
id: "fake-space-id",
2951
homepage: buildFakeContent(),
2952
},
2953
status: "current",
2954
title: "fake-title2",
2955
type: "page",
2956
},
2957
];
2958
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
2959
fileMetadataList,
2960
FAKE_PARENT,
2961
fakeSpace,
2962
existingSite
2963
);
2964
assertEquals(expected, actual);
2965
});
2966
2967
test(suiteLabel("from_root_to_existing_parent"), async () => {
2968
const fileMetadataList: SiteFileMetadata[] = [
2969
fakeNestedFile,
2970
fakeNestedFile2,
2971
];
2972
const existingSite = [
2973
{
2974
id: "fake-title-id",
2975
title: "fake-title",
2976
metadata: { fileName: "fake-file-name.xml" },
2977
ancestors: [{ id: "fake-grand-parent-id" }],
2978
},
2979
{
2980
id: "fake-title2-id",
2981
title: "fake-title",
2982
metadata: { fileName: "fake-parent/fake-file-name2.xml" },
2983
ancestors: [{ id: "fake-grand-parent-id" }, { id: "fake-parent-id" }],
2984
},
2985
{
2986
title: "Fake Parent",
2987
id: "fake-parent-id",
2988
metadata: { editor: "v2", fileName: "fake-parent" },
2989
ancestors: [{ id: "fake-grand-parent-id" }],
2990
},
2991
];
2992
2993
const expected: ConfluenceSpaceChange[] = [
2994
{ contentChangeType: ContentChangeType.delete, id: "fake-title-id" },
2995
{
2996
contentChangeType: ContentChangeType.create,
2997
ancestors: [
2998
{
2999
id: "fake-parent-id",
3000
},
3001
],
3002
body: {
3003
storage: {
3004
representation: "storage",
3005
value: "fake-value",
3006
},
3007
},
3008
fileName: "fake-parent/fake-file-name.xml",
3009
space: {
3010
key: "fake-space-key",
3011
id: "fake-space-id",
3012
homepage: buildFakeContent(),
3013
},
3014
status: "current",
3015
title: "fake-title",
3016
type: "page",
3017
},
3018
{
3019
contentChangeType: ContentChangeType.update,
3020
id: "fake-title2-id",
3021
version: null,
3022
title: "fake-title2",
3023
type: "page",
3024
status: "current",
3025
ancestors: [{ id: "fake-parent-id" }],
3026
body: { storage: { value: "fake-value", representation: "storage" } },
3027
fileName: "fake-parent/fake-file-name2.xml",
3028
},
3029
];
3030
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
3031
fileMetadataList,
3032
FAKE_PARENT,
3033
fakeSpace,
3034
existingSite
3035
);
3036
assertEquals(expected, actual);
3037
});
3038
3039
test(suiteLabel("create_in_nested_parent"), async () => {
3040
const fileMetadataList: SiteFileMetadata[] = [
3041
fakeNestedFile,
3042
fakeMultiNestedFile,
3043
];
3044
const existingSite = [
3045
{
3046
id: "fake-title-id",
3047
title: "fake-title",
3048
metadata: { fileName: "fake-parent/fake-file-name.xml" },
3049
ancestors: [{ id: "fake-grand-parent-id" }, { id: "fake-parent-id" }],
3050
},
3051
{
3052
title: "Fake Parent",
3053
id: "fake-parent-id",
3054
metadata: { editor: "v2", fileName: "fake-parent" },
3055
ancestors: [{ id: "fake-grand-parent-id" }],
3056
},
3057
];
3058
3059
const expected: ConfluenceSpaceChange[] = [
3060
{
3061
contentChangeType: ContentChangeType.update,
3062
id: "fake-title-id",
3063
version: null,
3064
title: "fake-title",
3065
type: "page",
3066
status: "current",
3067
ancestors: [{ id: "fake-parent-id" }],
3068
body: { storage: { value: "fake-value", representation: "storage" } },
3069
fileName: "fake-parent/fake-file-name.xml",
3070
},
3071
{
3072
contentChangeType: ContentChangeType.create,
3073
title: "Fake-inner-parent",
3074
type: "page",
3075
ancestors: [
3076
{
3077
id: "fake-parent-id",
3078
},
3079
],
3080
body: {
3081
storage: {
3082
representation: "storage",
3083
value: "",
3084
},
3085
},
3086
fileName: "fake-parent/fake-inner-parent",
3087
space: {
3088
key: "fake-space-key",
3089
id: "fake-space-id",
3090
homepage: buildFakeContent(),
3091
},
3092
status: "current",
3093
},
3094
{
3095
contentChangeType: ContentChangeType.create,
3096
title: "fake-multi-nested-title",
3097
type: "page",
3098
ancestors: [
3099
{
3100
id: "fake-parent/fake-inner-parent",
3101
},
3102
],
3103
body: {
3104
storage: {
3105
representation: "storage",
3106
value: "fake-value",
3107
},
3108
},
3109
fileName: "fake-parent/fake-inner-parent/fake-file-name.xml",
3110
space: {
3111
key: "fake-space-key",
3112
id: "fake-space-id",
3113
homepage: buildFakeContent(),
3114
},
3115
status: "current",
3116
},
3117
];
3118
const actual: ConfluenceSpaceChange[] = buildSpaceChanges(
3119
fileMetadataList,
3120
FAKE_PARENT,
3121
fakeSpace,
3122
existingSite
3123
);
3124
assertEquals(expected, actual);
3125
});
3126
};
3127
3128
const runFlattenIndexes = () => {
3129
const suiteLabel = (label: string) => `FlattenIndexes_${label}`;
3130
3131
const FAKE_SITE_PARENT_ID = "fake-site-parent-id";
3132
3133
const FAKE_METADATA_ONE_FOLDER = {
3134
["fake-parent"]: {
3135
title: "Fake Parent Title",
3136
id: "fake-parent-id",
3137
metadata: {
3138
fileName: "fake-parent",
3139
},
3140
ancestors: [
3141
{
3142
id: "fake-space-id",
3143
},
3144
],
3145
},
3146
};
3147
3148
const FAKE_METADATA_EMPTY = {};
3149
3150
test(suiteLabel("no_files"), async () => {
3151
const spaceChanges: ConfluenceSpaceChange[] = [];
3152
const expected: ConfluenceSpaceChange[] = [];
3153
const actual: ConfluenceSpaceChange[] = flattenIndexes(
3154
spaceChanges,
3155
FAKE_METADATA_EMPTY,
3156
FAKE_SITE_PARENT_ID
3157
);
3158
assertEquals(expected, actual);
3159
});
3160
3161
test(suiteLabel("no_folders"), async () => {
3162
const spaceChanges: ConfluenceSpaceChange[] = [
3163
{
3164
contentChangeType: ContentChangeType.create,
3165
ancestors: [
3166
{
3167
id: "8781825",
3168
},
3169
],
3170
body: {
3171
storage: {
3172
representation: "storage",
3173
value: "fake-value",
3174
},
3175
},
3176
fileName: "fake-file-name",
3177
space: {
3178
key: "fake-space-key",
3179
id: "fake-space-id",
3180
homepage: buildFakeContent(),
3181
},
3182
status: "current",
3183
title: "fake-title",
3184
type: "page",
3185
},
3186
];
3187
const expected: ConfluenceSpaceChange[] = spaceChanges;
3188
const actual: ConfluenceSpaceChange[] = flattenIndexes(
3189
spaceChanges,
3190
FAKE_METADATA_EMPTY,
3191
FAKE_SITE_PARENT_ID
3192
);
3193
assertEquals(expected, actual);
3194
});
3195
3196
test(suiteLabel("one_folder_no_index"), async () => {
3197
const spaceChanges: ConfluenceSpaceChange[] = [
3198
{
3199
contentChangeType: ContentChangeType.create,
3200
ancestors: [
3201
{
3202
id: "8781825",
3203
},
3204
],
3205
body: {
3206
storage: {
3207
representation: "storage",
3208
value: "",
3209
},
3210
},
3211
fileName: "fake-parent",
3212
space: {
3213
key: "fake-space-key",
3214
id: "fake-space-id",
3215
homepage: buildFakeContent(),
3216
},
3217
status: "current",
3218
title: "Fake-parent",
3219
type: "page",
3220
},
3221
{
3222
contentChangeType: ContentChangeType.create,
3223
ancestors: [
3224
{
3225
id: "fake-parent",
3226
},
3227
],
3228
body: {
3229
storage: {
3230
representation: "storage",
3231
value: "fake-value",
3232
},
3233
},
3234
fileName: "fake-parent/fake-file-name",
3235
space: {
3236
key: "fake-space-key",
3237
id: "fake-space-id",
3238
homepage: buildFakeContent(),
3239
},
3240
status: "current",
3241
title: "fake-title",
3242
type: "page",
3243
},
3244
];
3245
const expected: ConfluenceSpaceChange[] = spaceChanges;
3246
const actual: ConfluenceSpaceChange[] = flattenIndexes(
3247
spaceChanges,
3248
FAKE_METADATA_EMPTY,
3249
FAKE_SITE_PARENT_ID
3250
);
3251
assertEquals(expected, actual);
3252
});
3253
3254
test(suiteLabel("create_one_folder_with_index"), async () => {
3255
const spaceChanges: ConfluenceSpaceChange[] = [
3256
{
3257
contentChangeType: ContentChangeType.create,
3258
ancestors: [
3259
{
3260
id: "8781825",
3261
},
3262
],
3263
body: {
3264
storage: {
3265
representation: "storage",
3266
value: "",
3267
},
3268
},
3269
fileName: "fake-parent",
3270
space: {
3271
key: "fake-space-key",
3272
id: "fake-space-id",
3273
homepage: buildFakeContent(),
3274
},
3275
status: "current",
3276
title: "Fake-parent",
3277
type: "page",
3278
},
3279
{
3280
contentChangeType: ContentChangeType.create,
3281
ancestors: [
3282
{
3283
id: "fake-parent",
3284
},
3285
],
3286
body: {
3287
storage: {
3288
representation: "storage",
3289
value: "fake-index-value",
3290
},
3291
},
3292
fileName: "fake-parent/index.xml",
3293
space: {
3294
key: "fake-space-key",
3295
id: "fake-space-id",
3296
homepage: buildFakeContent(),
3297
},
3298
status: "current",
3299
title: "fake-index-title",
3300
type: "page",
3301
},
3302
];
3303
const expected: ConfluenceSpaceChange[] = [
3304
{
3305
contentChangeType: ContentChangeType.create,
3306
ancestors: [
3307
{
3308
id: "8781825",
3309
},
3310
],
3311
body: {
3312
storage: {
3313
representation: "storage",
3314
value: "fake-index-value",
3315
},
3316
},
3317
fileName: "fake-parent",
3318
space: {
3319
key: "fake-space-key",
3320
id: "fake-space-id",
3321
homepage: buildFakeContent(),
3322
},
3323
status: "current",
3324
title: "fake-index-title",
3325
type: "page",
3326
},
3327
];
3328
const actual: ConfluenceSpaceChange[] = flattenIndexes(
3329
spaceChanges,
3330
FAKE_METADATA_EMPTY,
3331
FAKE_SITE_PARENT_ID
3332
);
3333
assertEquals(expected, actual);
3334
});
3335
3336
test(suiteLabel("create_root_with_index"), async () => {
3337
const spaceChanges: ConfluenceSpaceChange[] = [
3338
{
3339
contentChangeType: ContentChangeType.create,
3340
ancestors: [
3341
{
3342
id: "8781825",
3343
},
3344
],
3345
body: {
3346
storage: {
3347
representation: "storage",
3348
value: "fake content root index",
3349
},
3350
},
3351
fileName: "index.xml",
3352
space: {
3353
key: "fake-space-key",
3354
id: "fake-space-id",
3355
homepage: buildFakeContent(),
3356
},
3357
status: "current",
3358
title: "Root Index",
3359
type: "page",
3360
},
3361
];
3362
const expected: ConfluenceSpaceChange[] = [
3363
{
3364
contentChangeType: ContentChangeType.update,
3365
id: FAKE_SITE_PARENT_ID,
3366
version: null,
3367
ancestors: null,
3368
body: {
3369
storage: {
3370
representation: "storage",
3371
value: "fake content root index",
3372
},
3373
},
3374
fileName: "index.xml",
3375
status: "current",
3376
title: "Root Index",
3377
type: "page",
3378
},
3379
];
3380
const actual: ConfluenceSpaceChange[] = flattenIndexes(
3381
spaceChanges,
3382
FAKE_METADATA_EMPTY,
3383
FAKE_SITE_PARENT_ID
3384
);
3385
assertEquals(expected, actual);
3386
});
3387
3388
test(suiteLabel("update_one_folder_with_index"), async () => {
3389
const spaceChanges: ConfluenceSpaceChange[] = [
3390
{
3391
contentChangeType: ContentChangeType.create,
3392
ancestors: [
3393
{
3394
id: "fake-parent-id",
3395
},
3396
],
3397
body: {
3398
storage: {
3399
representation: "storage",
3400
value: "fake-index-value-update",
3401
},
3402
},
3403
fileName: "fake-parent/index.xml",
3404
space: {
3405
key: "fake-space-key",
3406
id: "fake-space-id",
3407
homepage: buildFakeContent(),
3408
},
3409
status: "current",
3410
title: "fake-index-title-update",
3411
type: "page",
3412
},
3413
];
3414
const expected: ConfluenceSpaceChange[] = [
3415
{
3416
contentChangeType: ContentChangeType.update,
3417
id: "fake-parent-id",
3418
version: null,
3419
ancestors: [
3420
{
3421
id: "fake-space-id",
3422
},
3423
],
3424
body: {
3425
storage: {
3426
representation: "storage",
3427
value: "fake-index-value-update",
3428
},
3429
},
3430
fileName: "fake-parent",
3431
status: "current",
3432
title: "fake-index-title-update",
3433
type: "page",
3434
},
3435
];
3436
const actual: ConfluenceSpaceChange[] = flattenIndexes(
3437
spaceChanges,
3438
FAKE_METADATA_ONE_FOLDER,
3439
FAKE_SITE_PARENT_ID
3440
);
3441
assertEquals(expected, actual);
3442
});
3443
3444
test(suiteLabel("one_multinested_folder_with_indexes"), async () => {
3445
const spaceChanges: ConfluenceSpaceChange[] = [
3446
{
3447
contentChangeType: ContentChangeType.create,
3448
ancestors: [
3449
{
3450
id: "8781825",
3451
},
3452
],
3453
body: {
3454
storage: {
3455
representation: "storage",
3456
value: "",
3457
},
3458
},
3459
fileName: "fake-great-grand-parent",
3460
space: {
3461
key: "fake-space-key",
3462
id: "fake-space-id",
3463
homepage: buildFakeContent(),
3464
},
3465
status: "current",
3466
title: "Fake-great-grand-parent",
3467
type: "page",
3468
},
3469
{
3470
contentChangeType: ContentChangeType.create,
3471
ancestors: [
3472
{
3473
id: "fake-great-grand-parent",
3474
},
3475
],
3476
body: {
3477
storage: {
3478
representation: "storage",
3479
value: "",
3480
},
3481
},
3482
fileName: "fake-great-grand-parent/fake-grand-parent",
3483
space: {
3484
key: "fake-space-key",
3485
id: "fake-space-id",
3486
homepage: buildFakeContent(),
3487
},
3488
status: "current",
3489
title: "Fake-grand-parent",
3490
type: "page",
3491
},
3492
{
3493
contentChangeType: ContentChangeType.create,
3494
ancestors: [
3495
{
3496
id: "fake-great-grand-parent/fake-grand-parent",
3497
},
3498
],
3499
body: {
3500
storage: {
3501
representation: "storage",
3502
value: "",
3503
},
3504
},
3505
fileName: "fake-great-grand-parent/fake-grand-parent/fake-parent",
3506
space: {
3507
key: "fake-space-key",
3508
id: "fake-space-id",
3509
homepage: buildFakeContent(),
3510
},
3511
status: "current",
3512
title: "Fake-parent",
3513
type: "page",
3514
},
3515
{
3516
contentChangeType: ContentChangeType.create,
3517
ancestors: [
3518
{
3519
id: "fake-great-grand-parent/fake-grand-parent/fake-parent",
3520
},
3521
],
3522
body: {
3523
storage: {
3524
representation: "storage",
3525
value: "fake-value",
3526
},
3527
},
3528
fileName:
3529
"fake-great-grand-parent/fake-grand-parent/fake-parent/index.xml",
3530
space: {
3531
key: "fake-space-key",
3532
id: "fake-space-id",
3533
homepage: buildFakeContent(),
3534
},
3535
status: "current",
3536
title: "fake-title",
3537
type: "page",
3538
},
3539
];
3540
const expected: ConfluenceSpaceChange[] = [
3541
{
3542
contentChangeType: ContentChangeType.create,
3543
ancestors: [
3544
{
3545
id: "8781825",
3546
},
3547
],
3548
body: {
3549
storage: {
3550
representation: "storage",
3551
value: "",
3552
},
3553
},
3554
fileName: "fake-great-grand-parent",
3555
space: {
3556
key: "fake-space-key",
3557
id: "fake-space-id",
3558
homepage: buildFakeContent(),
3559
},
3560
status: "current",
3561
title: "Fake-great-grand-parent",
3562
type: "page",
3563
},
3564
{
3565
contentChangeType: ContentChangeType.create,
3566
ancestors: [
3567
{
3568
id: "fake-great-grand-parent",
3569
},
3570
],
3571
body: {
3572
storage: {
3573
representation: "storage",
3574
value: "",
3575
},
3576
},
3577
fileName: "fake-great-grand-parent/fake-grand-parent",
3578
space: {
3579
key: "fake-space-key",
3580
id: "fake-space-id",
3581
homepage: buildFakeContent(),
3582
},
3583
status: "current",
3584
title: "Fake-grand-parent",
3585
type: "page",
3586
},
3587
{
3588
contentChangeType: ContentChangeType.create,
3589
ancestors: [
3590
{
3591
id: "fake-great-grand-parent/fake-grand-parent",
3592
},
3593
],
3594
body: {
3595
storage: {
3596
representation: "storage",
3597
value: "fake-value",
3598
},
3599
},
3600
fileName: "fake-great-grand-parent/fake-grand-parent/fake-parent",
3601
space: {
3602
key: "fake-space-key",
3603
id: "fake-space-id",
3604
homepage: buildFakeContent(),
3605
},
3606
status: "current",
3607
title: "fake-title",
3608
type: "page",
3609
},
3610
];
3611
const actual: ConfluenceSpaceChange[] = flattenIndexes(
3612
spaceChanges,
3613
FAKE_METADATA_EMPTY,
3614
FAKE_SITE_PARENT_ID
3615
);
3616
assertEquals(expected, actual);
3617
});
3618
};
3619
3620
const runBuildFileToMetaTable = () => {
3621
const suiteLabel = (label: string) => `BuildFileToMetaTable_${label}`;
3622
3623
const fakeSite: SitePage[] = [
3624
{
3625
title: "Issue Triage",
3626
id: "19890180",
3627
metadata: {
3628
fileName: "triage.xml",
3629
},
3630
},
3631
{
3632
title: "Release Planning",
3633
id: "19890228",
3634
metadata: { fileName: "release-planning.xml" },
3635
},
3636
{
3637
title: "Team",
3638
id: "19857455",
3639
metadata: { fileName: "team.xml" },
3640
},
3641
];
3642
3643
const fakeSite_ONE: SitePage[] = [
3644
{
3645
title: "Issue Triage",
3646
id: "19890180",
3647
metadata: {
3648
fileName: "triage.xml",
3649
},
3650
},
3651
];
3652
3653
const check = (expected: Record<string, SitePage>, site: SitePage[]) => {
3654
assertEquals(expected, buildFileToMetaTable(site));
3655
};
3656
3657
test(suiteLabel("no_files"), async () => {
3658
const expected = {};
3659
const site: SitePage[] = [];
3660
check(expected, site);
3661
});
3662
3663
test(suiteLabel("one_file"), async () => {
3664
const expected = {
3665
["triage.qmd"]: {
3666
title: "Issue Triage",
3667
id: "19890180",
3668
metadata: {
3669
fileName: "triage.xml",
3670
},
3671
},
3672
};
3673
check(expected, fakeSite_ONE);
3674
});
3675
3676
test(suiteLabel("multiple"), async () => {
3677
const expected = {
3678
["release-planning.qmd"]: {
3679
title: "Release Planning",
3680
id: "19890228",
3681
metadata: { fileName: "release-planning.xml" },
3682
},
3683
["team.qmd"]: {
3684
title: "Team",
3685
id: "19857455",
3686
metadata: { fileName: "team.xml" },
3687
},
3688
["triage.qmd"]: {
3689
title: "Issue Triage",
3690
id: "19890180",
3691
metadata: {
3692
fileName: "triage.xml",
3693
},
3694
},
3695
};
3696
check(expected, fakeSite);
3697
});
3698
};
3699
3700
const runExtractLinks = () => {
3701
const suiteLabel = (label: string) => `ExtractLinks_${label}`;
3702
3703
const extractLinks = (value: string): ExtractedLink[] => {
3704
const links: string[] = value.match(LINK_FINDER) ?? [];
3705
const extractedLinks: ExtractedLink[] = links.map(
3706
(link: string): ExtractedLink => {
3707
const fileMatches = link.match(FILE_FINDER);
3708
const file = fileMatches ? fileMatches[0] ?? "" : "";
3709
return { link, file: `${file}.qmd` };
3710
}
3711
);
3712
return extractedLinks;
3713
};
3714
3715
const check = (expected: ExtractedLink[], value: string) => {
3716
assertEquals(expected, extractLinks(value));
3717
};
3718
3719
test(suiteLabel("empty_string"), async () => {
3720
const value = "";
3721
const expected: ExtractedLink[] = [];
3722
check(expected, value);
3723
});
3724
3725
test(suiteLabel("three_links"), async () => {
3726
const value =
3727
"<a href='no-replace.qmd'/>no</a> content content <a href='team.qmd#Fake-Anchor'>team</a> content content <a href='zqmdzz.qmd'>team</a>";
3728
const expected: ExtractedLink[] = [
3729
{ link: "href='no-replace.qmd'", file: "no-replace.qmd" },
3730
{ link: "href='team.qmd#Fake-Anchor'", file: "team.qmd" },
3731
{ link: "href='zqmdzz.qmd'", file: "zqmdzz.qmd" },
3732
];
3733
check(expected, value);
3734
});
3735
3736
test(suiteLabel("three_links_messy"), async () => {
3737
const value =
3738
"no-replace.qmd <a href='no-replace.qmd'/>no</a> no-replace.qmd content content <a href='team.qmd#Fake-Anchor'>team</a> content content <a href='zqmdzz.qmd'>team</a> qmd.qmd <a href='team.txt'>team</a>";
3739
const expected: ExtractedLink[] = [
3740
{ link: "href='no-replace.qmd'", file: "no-replace.qmd" },
3741
{ link: "href='team.qmd#Fake-Anchor'", file: "team.qmd" },
3742
{ link: "href='zqmdzz.qmd'", file: "zqmdzz.qmd" },
3743
];
3744
check(expected, value);
3745
});
3746
};
3747
3748
const runUpdateLinks = () => {
3749
const suiteLabel = (label: string) => `UpdateLinks_${label}`;
3750
3751
const fileMetadataTable = {
3752
["release-planning.qmd"]: {
3753
title: "Release Planning",
3754
id: "19890228",
3755
metadata: { fileName: "release-planning.xml" },
3756
},
3757
["team.qmd"]: {
3758
title: "Team",
3759
id: "19857455",
3760
metadata: { fileName: "team.xml" },
3761
},
3762
["triage.qmd"]: {
3763
title: "Issue Triage",
3764
id: "19890180",
3765
metadata: {
3766
fileName: "triage.xml",
3767
},
3768
},
3769
["authoring/hello-world5.qmd"]: {
3770
title: "Hello World5",
3771
id: "43417628",
3772
metadata: { editor: "v2", fileName: "authoring/hello-world5.xml" },
3773
},
3774
["special-characters.qmd"]: {
3775
title: "Special Characters !@#$%^&*(){}|?//\\",
3776
id: "123456",
3777
metadata: { fileName: "special-characters.xml" },
3778
},
3779
["folder/index.qmd"]: {
3780
title: "fake-index-title",
3781
id: "fake-index-id",
3782
metadata: { editor: "v2", fileName: "folder/index.xml" },
3783
},
3784
["folder"]: {
3785
title: "fake-folder-title",
3786
id: "fake-folder-id",
3787
metadata: { editor: "v2", fileName: "folder" },
3788
},
3789
};
3790
3791
const UPDATE_NO_LINKS: ContentUpdate = {
3792
contentChangeType: ContentChangeType.update,
3793
id: "19890228",
3794
version: null,
3795
title: "Release Planning",
3796
type: "page",
3797
status: "current",
3798
ancestors: [{ id: "19759105" }],
3799
body: {
3800
storage: {
3801
value: "no links",
3802
representation: "storage",
3803
},
3804
},
3805
fileName: "release-planning.xml",
3806
};
3807
3808
const UPDATE_LINKS_ONE: ContentUpdate = {
3809
contentChangeType: ContentChangeType.update,
3810
id: "19890228",
3811
version: null,
3812
title: "Release Planning",
3813
type: "page",
3814
status: "current",
3815
ancestors: [{ id: "19759105" }],
3816
body: {
3817
storage: {
3818
value:
3819
"<a href='no-replace.qmd'/>no</a> content content <a href='team.qmd'>team</a> content content <a href='zqmdzz.qmd'>team</a>",
3820
representation: "storage",
3821
},
3822
},
3823
fileName: "release-planning.xml",
3824
};
3825
3826
const UPDATE_LINK_TO_INDEX: ContentUpdate = {
3827
contentChangeType: ContentChangeType.update,
3828
id: "19890228",
3829
version: null,
3830
title: "Release Planning",
3831
type: "page",
3832
status: "current",
3833
ancestors: [{ id: "19759105" }],
3834
body: {
3835
storage: {
3836
value: "<a href='folder/index.qmd'>team</a>",
3837
representation: "storage",
3838
},
3839
},
3840
fileName: "release-planning.xml",
3841
};
3842
3843
const UPDATE_SELF_LINK_FROM_INDEX: ContentUpdate = {
3844
contentChangeType: ContentChangeType.update,
3845
id: "fake-folder-id",
3846
version: null,
3847
title: "fake-folder-title",
3848
type: "page",
3849
status: "current",
3850
ancestors: [{ id: "19759105" }],
3851
body: {
3852
storage: {
3853
value: "<a href='index.qmd'>self</a>",
3854
representation: "storage",
3855
},
3856
},
3857
fileName: "folder",
3858
};
3859
3860
const UPDATE_LINKS_SPECIAL_CHAR: ContentUpdate = {
3861
contentChangeType: ContentChangeType.update,
3862
id: "19890228",
3863
version: null,
3864
title: "Release Planning",
3865
type: "page",
3866
status: "current",
3867
ancestors: [{ id: "19759105" }],
3868
body: {
3869
storage: {
3870
value: "<a href='special-characters.qmd'>team</a>",
3871
representation: "storage",
3872
},
3873
},
3874
fileName: "release-planning.xml",
3875
};
3876
3877
const UPDATE_LINKS_ONE_NESTED_DOT_SLASH: ContentUpdate = {
3878
contentChangeType: ContentChangeType.update,
3879
id: "43778049",
3880
version: null,
3881
title: "Links2",
3882
type: "page",
3883
status: "current",
3884
ancestors: [{ id: "42336414" }],
3885
body: {
3886
storage: {
3887
value: `<a href='./hello-world5.qmd'>Hello World 5</a>`,
3888
representation: "storage",
3889
},
3890
},
3891
fileName: "authoring/links2.xml",
3892
};
3893
3894
const UPDATE_LINKS_ONE_NESTED: ContentUpdate = {
3895
contentChangeType: ContentChangeType.update,
3896
id: "43778049",
3897
version: null,
3898
title: "Links2",
3899
type: "page",
3900
status: "current",
3901
ancestors: [{ id: "42336414" }],
3902
body: {
3903
storage: {
3904
value: `<a href='hello-world5.qmd'>Hello World 5</a>`,
3905
representation: "storage",
3906
},
3907
},
3908
fileName: "authoring/links2.xml",
3909
};
3910
3911
const UPDATE_LINKS_ONE_NESTED_ABS: ContentUpdate = {
3912
contentChangeType: ContentChangeType.update,
3913
id: "43778049",
3914
version: null,
3915
title: "Links2",
3916
type: "page",
3917
status: "current",
3918
ancestors: [{ id: "42336414" }],
3919
body: {
3920
storage: {
3921
value: `<a href='/release-planning.qmd'>Release Planning</a>`,
3922
representation: "storage",
3923
},
3924
},
3925
fileName: "authoring/links2.xml",
3926
};
3927
3928
const UPDATE_LINKS_ONE_ANCHOR: ContentUpdate = {
3929
contentChangeType: ContentChangeType.update,
3930
id: "19890228",
3931
version: null,
3932
title: "Release Planning",
3933
type: "page",
3934
status: "current",
3935
ancestors: [{ id: "19759105" }],
3936
body: {
3937
storage: {
3938
value:
3939
"<a href='no-replace.qmd'/>no</a> content content <a href='team.qmd#Fake-Anchor'>team</a> content content <a href='zqmdzz.qmd'>team</a>",
3940
representation: "storage",
3941
},
3942
},
3943
fileName: "release-planning.xml",
3944
};
3945
3946
const UPDATE_LINKS_SEVERAL: ContentUpdate = {
3947
contentChangeType: ContentChangeType.update,
3948
id: "19890228",
3949
version: null,
3950
title: "Release Planning",
3951
type: "page",
3952
status: "current",
3953
ancestors: [{ id: "19759105" }],
3954
body: {
3955
storage: {
3956
value:
3957
"<a href='no-replace.qmd'/>not-found</a> content content <a href='team.qmd'>teamz</a> content content <a href='zqmdzz.qmd'>not-found</a> <a href='release-planning.qmd'>Do the Release Planning</a> and then triage.qmd .qmd <a href='triage.qmd'>triage.qmd</a>",
3958
representation: "storage",
3959
},
3960
},
3961
fileName: "release-planning.xml",
3962
};
3963
3964
const check = (
3965
expectedPass1Changes: ConfluenceSpaceChange[],
3966
changes: ConfluenceSpaceChange[],
3967
fileMetadataTable: Record<string, SitePage>,
3968
server = "fake-server",
3969
parent = FAKE_PARENT,
3970
expectedPass2Changes: ConfluenceSpaceChange[] = []
3971
) => {
3972
const { pass1Changes, pass2Changes } = updateLinks(
3973
fileMetadataTable,
3974
changes,
3975
server,
3976
parent
3977
);
3978
3979
assertEquals(expectedPass1Changes, pass1Changes);
3980
assertEquals(expectedPass2Changes, pass2Changes);
3981
};
3982
3983
test(suiteLabel("no_files"), async () => {
3984
const changes: ConfluenceSpaceChange[] = [];
3985
const expected: ConfluenceSpaceChange[] = [];
3986
check(expected, changes, fileMetadataTable);
3987
});
3988
3989
test(suiteLabel("one_update_noLink"), async () => {
3990
const changes: ConfluenceSpaceChange[] = [UPDATE_NO_LINKS];
3991
const expected: ConfluenceSpaceChange[] = [UPDATE_NO_LINKS];
3992
check(expected, changes, fileMetadataTable);
3993
});
3994
3995
test(suiteLabel("one_update_link"), async () => {
3996
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_ONE];
3997
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
3998
const expectedUpdate: ContentUpdate = {
3999
...UPDATE_LINKS_ONE,
4000
body: {
4001
storage: {
4002
value: `<a href=\'no-replace.qmd\'/>no</a> content content <a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/19857455'>team</a> content content <a href=\'zqmdzz.qmd\'>team</a>`,
4003
representation: "storage",
4004
},
4005
},
4006
};
4007
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4008
const expectedPass2Changes: ConfluenceSpaceChange[] = [expectedUpdate];
4009
check(
4010
expected,
4011
changes,
4012
fileMetadataTable,
4013
"fake-server",
4014
FAKE_PARENT,
4015
expectedPass2Changes
4016
);
4017
});
4018
4019
test(suiteLabel("one_update_link_index"), async () => {
4020
const changes: ConfluenceSpaceChange[] = [UPDATE_LINK_TO_INDEX];
4021
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4022
const expectedUpdate: ContentUpdate = {
4023
...UPDATE_LINK_TO_INDEX,
4024
body: {
4025
storage: {
4026
value: `<a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/fake-folder-id'>team</a>`,
4027
representation: "storage",
4028
},
4029
},
4030
};
4031
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4032
check(expected, changes, fileMetadataTable, "fake-server", FAKE_PARENT);
4033
});
4034
4035
test(suiteLabel("one_update_link_from_index"), async () => {
4036
const changes: ConfluenceSpaceChange[] = [UPDATE_SELF_LINK_FROM_INDEX];
4037
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4038
const expectedUpdate: ContentUpdate = {
4039
...UPDATE_SELF_LINK_FROM_INDEX,
4040
body: {
4041
storage: {
4042
value: `<a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/fake-index-id'>self</a>`,
4043
representation: "storage",
4044
},
4045
},
4046
};
4047
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4048
check(expected, changes, fileMetadataTable, "fake-server", FAKE_PARENT);
4049
});
4050
4051
test(suiteLabel("one_update_link_special_char"), async () => {
4052
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_SPECIAL_CHAR];
4053
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4054
const expectedUpdate: ContentUpdate = {
4055
...UPDATE_LINKS_ONE,
4056
body: {
4057
storage: {
4058
value: `<a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/123456'>team</a>`,
4059
representation: "storage",
4060
},
4061
},
4062
};
4063
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4064
check(expected, changes, fileMetadataTable, "fake-server", FAKE_PARENT);
4065
});
4066
4067
test(suiteLabel("one_update_link_nested_dot_slash"), async () => {
4068
const changes: ConfluenceSpaceChange[] = [
4069
UPDATE_LINKS_ONE_NESTED_DOT_SLASH,
4070
];
4071
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4072
const expectedUpdate: ContentUpdate = {
4073
...UPDATE_LINKS_ONE_NESTED_DOT_SLASH,
4074
body: {
4075
storage: {
4076
value: `<a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/43417628'>Hello World 5</a>`,
4077
representation: "storage",
4078
},
4079
},
4080
};
4081
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4082
check(expected, changes, fileMetadataTable);
4083
});
4084
4085
test(suiteLabel("one_update_link_nested_relative"), async () => {
4086
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_ONE_NESTED];
4087
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4088
const expectedUpdate: ContentUpdate = {
4089
...UPDATE_LINKS_ONE_NESTED,
4090
body: {
4091
storage: {
4092
value: `<a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/43417628'>Hello World 5</a>`,
4093
representation: "storage",
4094
},
4095
},
4096
};
4097
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4098
check(expected, changes, fileMetadataTable);
4099
});
4100
4101
test(suiteLabel("one_update_link_nested_absolute"), async () => {
4102
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_ONE_NESTED_ABS];
4103
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4104
const expectedUpdate: ContentUpdate = {
4105
...UPDATE_LINKS_ONE_NESTED_ABS,
4106
body: {
4107
storage: {
4108
value: `<a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/19890228'>Release Planning</a>`,
4109
representation: "storage",
4110
},
4111
},
4112
};
4113
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4114
check(expected, changes, fileMetadataTable);
4115
});
4116
4117
test(suiteLabel("one_update_link_anchor"), async () => {
4118
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_ONE_ANCHOR];
4119
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4120
const expectedUpdate: ContentUpdate = {
4121
...UPDATE_LINKS_ONE,
4122
body: {
4123
storage: {
4124
value:
4125
"<a href='no-replace.qmd'/>no</a> content content <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19857455#Fake-Anchor'>team</a> content content <a href='zqmdzz.qmd'>team</a>",
4126
representation: "storage",
4127
},
4128
},
4129
};
4130
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4131
const expectedSecondPassChanges: ConfluenceSpaceChange[] = [expectedUpdate];
4132
check(
4133
expected,
4134
changes,
4135
fileMetadataTable,
4136
"fake-server",
4137
FAKE_PARENT,
4138
expectedSecondPassChanges
4139
);
4140
});
4141
4142
test(suiteLabel("one_change_several_update_links"), async () => {
4143
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_SEVERAL];
4144
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4145
const expectedUpdate: ContentUpdate = {
4146
...UPDATE_LINKS_ONE,
4147
body: {
4148
storage: {
4149
value: `<a href='no-replace.qmd'/>not-found</a> content content <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19857455'>teamz</a> content content <a href='zqmdzz.qmd'>not-found</a> <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19890228'>Do the Release Planning</a> and then triage.qmd .qmd <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19890180'>triage.qmd</a>`,
4150
representation: "storage",
4151
},
4152
},
4153
};
4154
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4155
const expectedSecondPassChanges: ConfluenceSpaceChange[] = [expectedUpdate];
4156
check(
4157
expected,
4158
changes,
4159
fileMetadataTable,
4160
"fake-server",
4161
FAKE_PARENT,
4162
expectedSecondPassChanges
4163
);
4164
});
4165
4166
test(suiteLabel("two_changes_several_update_links"), async () => {
4167
const changes: ConfluenceSpaceChange[] = [
4168
UPDATE_LINKS_SEVERAL,
4169
UPDATE_LINKS_ONE,
4170
];
4171
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4172
const expectedUpdateSeveralLinks: ContentUpdate = {
4173
...UPDATE_LINKS_ONE,
4174
body: {
4175
storage: {
4176
value: `<a href='no-replace.qmd'/>not-found</a> content content <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19857455'>teamz</a> content content <a href='zqmdzz.qmd'>not-found</a> <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19890228'>Do the Release Planning</a> and then triage.qmd .qmd <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19890180'>triage.qmd</a>`,
4177
representation: "storage",
4178
},
4179
},
4180
};
4181
4182
const expectedUpdateOneLink: ContentUpdate = {
4183
...UPDATE_LINKS_ONE,
4184
body: {
4185
storage: {
4186
value: `<a href='no-replace.qmd'/>no</a> content content <a href='fake-server/wiki/spaces/QUARTOCONF/pages/19857455'>team</a> content content <a href='zqmdzz.qmd'>team</a>`,
4187
representation: "storage",
4188
},
4189
},
4190
};
4191
const expected: ConfluenceSpaceChange[] = [
4192
expectedUpdateSeveralLinks,
4193
expectedUpdateOneLink,
4194
];
4195
const expectedSecondPassChanges: ConfluenceSpaceChange[] = [
4196
expectedUpdateSeveralLinks,
4197
expectedUpdateOneLink,
4198
];
4199
check(
4200
expected,
4201
changes,
4202
fileMetadataTable,
4203
"fake-server",
4204
FAKE_PARENT,
4205
expectedSecondPassChanges
4206
);
4207
});
4208
};
4209
4210
const runConvertForSecondPass = () => {
4211
const suiteLabel = (label: string) => `ConvertForSecondPass_${label}`;
4212
4213
const fileMetadataTable = {
4214
["release-planning.qmd"]: {
4215
title: "Release Planning",
4216
id: "19890228",
4217
metadata: { fileName: "release-planning.xml" },
4218
},
4219
["team.qmd"]: {
4220
title: "Team",
4221
id: "19857455",
4222
metadata: { fileName: "team.xml" },
4223
},
4224
["team.test.qmd"]: {
4225
title: "TeamTest",
4226
id: "19857456",
4227
metadata: { fileName: "team.test.xml" },
4228
},
4229
["triage.qmd"]: {
4230
title: "Issue Triage",
4231
id: "19890180",
4232
metadata: {
4233
fileName: "triage.xml",
4234
},
4235
},
4236
["authoring/hello-world5.qmd"]: {
4237
title: "Hello World5",
4238
id: "43417628",
4239
metadata: { editor: "v2", fileName: "authoring/hello-world5.xml" },
4240
},
4241
};
4242
4243
const fakeSpace: Space = {
4244
key: "fake-space-key",
4245
id: "fake-space-id",
4246
homepage: buildFakeContent(),
4247
};
4248
4249
const UPDATE_NO_LINKS: ContentUpdate = {
4250
contentChangeType: ContentChangeType.update,
4251
id: "19890228",
4252
version: null,
4253
title: "Release Planning",
4254
type: "page",
4255
status: "current",
4256
ancestors: [{ id: "19759105" }],
4257
body: {
4258
storage: {
4259
value: "no links",
4260
representation: "storage",
4261
},
4262
},
4263
fileName: "release-planning.xml",
4264
};
4265
4266
const CREATE_NO_LINKS: ContentCreate = {
4267
contentChangeType: ContentChangeType.create,
4268
title: "Release Planning",
4269
type: "page",
4270
status: "current",
4271
ancestors: [{ id: "19759105" }],
4272
space: fakeSpace,
4273
body: {
4274
storage: {
4275
value: "no links",
4276
representation: "storage",
4277
},
4278
},
4279
fileName: "release-planning.xml",
4280
};
4281
4282
const UPDATE_LINKS_ONE: ContentUpdate = {
4283
contentChangeType: ContentChangeType.update,
4284
id: "19890228",
4285
version: null,
4286
title: "Release Planning",
4287
type: "page",
4288
status: "current",
4289
ancestors: [{ id: "19759105" }],
4290
body: {
4291
storage: {
4292
value:
4293
"<a href='no-replace.qmd'/>no</a> content content <a href='team.qmd'>team</a> content content <a href='zqmdzz.qmd'>team</a>",
4294
representation: "storage",
4295
},
4296
},
4297
fileName: "release-planning.xml",
4298
};
4299
4300
const UPDATE_LINKS_ONE_DOUBLE_EXT: ContentUpdate = {
4301
...UPDATE_LINKS_ONE,
4302
body: {
4303
storage: {
4304
value:
4305
"<a href='no-replace.qmd'/>no</a> content content <a href='team.test.qmd'>team</a> content content <a href='zqmdzz.qmd'>team</a>",
4306
representation: "storage",
4307
},
4308
},
4309
};
4310
4311
const CREATE_LINKS_ONE: ContentCreate = {
4312
contentChangeType: ContentChangeType.create,
4313
title: "Release Planning",
4314
type: "page",
4315
status: "current",
4316
ancestors: [{ id: "19759105" }],
4317
space: fakeSpace,
4318
body: {
4319
storage: {
4320
value:
4321
"<a href='no-replace.qmd'/>no</a> content content <a href='team.qmd'>team</a> content content <a href='zqmdzz.qmd'>team</a>",
4322
representation: "storage",
4323
},
4324
},
4325
fileName: "release-planning.xml",
4326
};
4327
4328
const UPDATE_LINKS_ONE_NESTED_DOT_SLASH: ContentUpdate = {
4329
contentChangeType: ContentChangeType.update,
4330
id: "43778049",
4331
version: null,
4332
title: "Links2",
4333
type: "page",
4334
status: "current",
4335
ancestors: [{ id: "42336414" }],
4336
body: {
4337
storage: {
4338
value: `<a href='./hello-world5.qmd'>Hello World 5</a>`,
4339
representation: "storage",
4340
},
4341
},
4342
fileName: "authoring/links2.xml",
4343
};
4344
4345
const UPDATE_LINKS_ONE_NESTED: ContentUpdate = {
4346
contentChangeType: ContentChangeType.update,
4347
id: "43778049",
4348
version: null,
4349
title: "Links2",
4350
type: "page",
4351
status: "current",
4352
ancestors: [{ id: "42336414" }],
4353
body: {
4354
storage: {
4355
value: `<a href='hello-world5.qmd'>Hello World 5</a>`,
4356
representation: "storage",
4357
},
4358
},
4359
fileName: "authoring/links2.xml",
4360
};
4361
4362
const UPDATE_LINKS_ONE_NESTED_ABS: ContentUpdate = {
4363
contentChangeType: ContentChangeType.update,
4364
id: "43778049",
4365
version: null,
4366
title: "Links2",
4367
type: "page",
4368
status: "current",
4369
ancestors: [{ id: "42336414" }],
4370
body: {
4371
storage: {
4372
value: `<a href='/release-planning.qmd'>Release Planning</a>`,
4373
representation: "storage",
4374
},
4375
},
4376
fileName: "authoring/links2.xml",
4377
};
4378
4379
const UPDATE_LINKS_ONE_ANCHOR: ContentUpdate = {
4380
contentChangeType: ContentChangeType.update,
4381
id: "19890228",
4382
version: null,
4383
title: "Release Planning",
4384
type: "page",
4385
status: "current",
4386
ancestors: [{ id: "19759105" }],
4387
body: {
4388
storage: {
4389
value:
4390
"<a href='no-replace.qmd'/>no</a> content content <a href='team.qmd#Fake-Anchor'>team</a> content content <a href='zqmdzz.qmd'>team</a>",
4391
representation: "storage",
4392
},
4393
},
4394
fileName: "release-planning.xml",
4395
};
4396
4397
const UPDATE_LINKS_SEVERAL: ContentUpdate = {
4398
contentChangeType: ContentChangeType.update,
4399
id: "19890228",
4400
version: null,
4401
title: "Release Planning",
4402
type: "page",
4403
status: "current",
4404
ancestors: [{ id: "19759105" }],
4405
body: {
4406
storage: {
4407
value:
4408
"<a href='no-replace.qmd'/>not-found</a> content content <a href='team.qmd'>teamz</a> content content <a href='zqmdzz.qmd'>not-found</a> <a href='release-planning.qmd'>Do the Release Planning</a> and then triage.qmd .qmd <a href='triage.qmd'>triage.qmd</a>",
4409
representation: "storage",
4410
},
4411
},
4412
fileName: "release-planning.xml",
4413
};
4414
4415
const check = (
4416
expected: ConfluenceSpaceChange[] = [],
4417
changes: ConfluenceSpaceChange[],
4418
fileMetadataTable: Record<string, SitePage>,
4419
server = "fake-server",
4420
parent = FAKE_PARENT
4421
) => {
4422
const result = convertForSecondPass(
4423
fileMetadataTable,
4424
changes,
4425
server,
4426
parent
4427
);
4428
assertEquals(expected, result);
4429
};
4430
4431
test(suiteLabel("no_files"), async () => {
4432
const changes: ConfluenceSpaceChange[] = [];
4433
const expected: ConfluenceSpaceChange[] = [];
4434
check(expected, changes, fileMetadataTable);
4435
});
4436
4437
test(suiteLabel("one_update_noLink"), async () => {
4438
const changes: ConfluenceSpaceChange[] = [UPDATE_NO_LINKS];
4439
const expected: ConfluenceSpaceChange[] = [UPDATE_NO_LINKS];
4440
check(expected, changes, fileMetadataTable);
4441
});
4442
4443
test(suiteLabel("one_create_noLink_convert_to_update"), async () => {
4444
const changes: ConfluenceSpaceChange[] = [CREATE_NO_LINKS];
4445
const expected: ConfluenceSpaceChange[] = [UPDATE_NO_LINKS];
4446
check(expected, changes, fileMetadataTable);
4447
});
4448
4449
test(suiteLabel("one_update_link"), async () => {
4450
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_ONE];
4451
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4452
const expectedUpdate: ContentUpdate = {
4453
...UPDATE_LINKS_ONE,
4454
body: {
4455
storage: {
4456
value: `<a href=\'no-replace.qmd\'/>no</a> content content <a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/19857455'>team</a> content content <a href=\'zqmdzz.qmd\'>team</a>`,
4457
representation: "storage",
4458
},
4459
},
4460
};
4461
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4462
check(expected, changes, fileMetadataTable, "fake-server", FAKE_PARENT);
4463
});
4464
4465
test(suiteLabel("one_update_link_double_QMD"), async () => {
4466
const changes: ConfluenceSpaceChange[] = [UPDATE_LINKS_ONE_DOUBLE_EXT];
4467
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4468
const expectedUpdate: ContentUpdate = {
4469
...UPDATE_LINKS_ONE,
4470
body: {
4471
storage: {
4472
value: `<a href=\'no-replace.qmd\'/>no</a> content content <a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/19857456'>team</a> content content <a href=\'zqmdzz.qmd\'>team</a>`,
4473
representation: "storage",
4474
},
4475
},
4476
};
4477
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4478
check(expected, changes, fileMetadataTable, "fake-server", FAKE_PARENT);
4479
});
4480
4481
test(suiteLabel("one_update_link_from_create"), async () => {
4482
const changes: ConfluenceSpaceChange[] = [CREATE_LINKS_ONE];
4483
const rootURL = "fake-server/wiki/spaces/QUARTOCONF/pages";
4484
const expectedUpdate: ContentUpdate = {
4485
...UPDATE_LINKS_ONE,
4486
body: {
4487
storage: {
4488
value: `<a href=\'no-replace.qmd\'/>no</a> content content <a href=\'fake-server/wiki/spaces/QUARTOCONF/pages/19857455'>team</a> content content <a href=\'zqmdzz.qmd\'>team</a>`,
4489
representation: "storage",
4490
},
4491
},
4492
};
4493
const expected: ConfluenceSpaceChange[] = [expectedUpdate];
4494
check(expected, changes, fileMetadataTable, "fake-server", FAKE_PARENT);
4495
});
4496
};
4497
4498
const runFindAttachments = () => {
4499
const suiteLabel = (label: string) => `FindAttachments_${label}`;
4500
4501
const check = (
4502
expected: string[],
4503
bodyValue: string,
4504
filePaths: string[] = [],
4505
path: string = ""
4506
) => {
4507
assertEquals(
4508
JSON.stringify(expected),
4509
JSON.stringify(findAttachments(bodyValue, filePaths, path))
4510
);
4511
};
4512
4513
test(suiteLabel("empty"), async () => {
4514
const bodyValue: string = "";
4515
const expected: string[] = [];
4516
check(expected, bodyValue);
4517
});
4518
4519
test(suiteLabel("no_attachment"), async () => {
4520
const bodyValue: string = "fake body value";
4521
const expected: string[] = [];
4522
check(expected, bodyValue);
4523
});
4524
4525
test(suiteLabel("no_attachment_CDATA"), async () => {
4526
const bodyValue: string =
4527
"<ac:plain-text-body> <![CDATA[![Caption](elephant.png)]</ac:plain-text-body><ac:plain-text-body>]> </ac:plain-text-body>";
4528
const expected: string[] = [];
4529
check(expected, bodyValue);
4530
});
4531
4532
test(suiteLabel("single_image"), async () => {
4533
const bodyValue: string =
4534
'<ri:attachment ri:filename="elephant.png" ri:version-at-save="1" />';
4535
const expected: string[] = ["elephant.png"];
4536
check(expected, bodyValue);
4537
});
4538
4539
test(suiteLabel("single_image_lookup"), async () => {
4540
const bodyValue: string =
4541
'<ri:attachment ri:filename="elephant.png" ri:version-at-save="1" />';
4542
const filePaths: string[] = ["fake-path/elephant.png"];
4543
const expected: string[] = ["elephant.png"];
4544
check(expected, bodyValue, filePaths);
4545
});
4546
4547
test(suiteLabel("single_image_lookup_full_path"), async () => {
4548
const bodyValue: string =
4549
'<ri:attachment ri:filename="computations-r_files/figure-publish/fig-airquality-1.png" ri:version-at-save="1" />';
4550
const filePaths: string[] = [
4551
"computations/r/computations-r_files/figure-publish/fig-airquality-1.png",
4552
];
4553
const expected: string[] = [
4554
"computations/r/computations-r_files/figure-publish/fig-airquality-1.png",
4555
];
4556
check(expected, bodyValue, filePaths, "computations/r/index.xml");
4557
});
4558
4559
test(suiteLabel("single_image_lookup_full_path_win"), async () => {
4560
const bodyValue: string =
4561
'<ri:attachment ri:filename="computations-r_files/figure-publish/fig-airquality-1.png" ri:version-at-save="1" />';
4562
const filePaths: string[] = [
4563
"computations\\r\\computations-r_files\\figure-publish\\fig-airquality-1.png",
4564
];
4565
const expected: string[] = [
4566
"computations\\r\\computations-r_files\\figure-publish\\fig-airquality-1.png",
4567
];
4568
check(expected, bodyValue, filePaths, "computations\\r\\index.xml");
4569
});
4570
4571
test(suiteLabel("single_with_same_name_nested_win"), async () => {
4572
const filePaths: string[] = [
4573
"folder\\images\\elephant.png",
4574
"images\\elephant.png",
4575
];
4576
const bodyValue: string =
4577
'<ri:attachment ri:filename="images/elephant.png" />';
4578
const expected: string[] = ["images\\elephant.png"];
4579
const path = "index.xml";
4580
check(expected, bodyValue, filePaths, path);
4581
});
4582
4583
test(suiteLabel("single_with_same_name_nested_win2"), async () => {
4584
const filePaths: string[] = [
4585
"folder\\images\\elephant.png",
4586
"images\\elephant.png",
4587
];
4588
const bodyValue: string =
4589
'<ri:attachment ri:filename="images/elephant.png" />';
4590
const expected: string[] = ["folder\\images\\elephant.png"];
4591
const path = "folder\\index.xml";
4592
check(expected, bodyValue, filePaths, path);
4593
});
4594
4595
test(suiteLabel("single_with_same_name_nested"), async () => {
4596
const filePaths: string[] = [
4597
"images/elephant.png",
4598
"folder/images/elephant.png",
4599
];
4600
const bodyValue: string =
4601
'<ri:attachment ri:filename="images/elephant.png" />';
4602
const expected: string[] = ["images/elephant.png"];
4603
const path = "index.xml";
4604
check(expected, bodyValue, filePaths, path);
4605
});
4606
4607
test(suiteLabel("single_image_lookup_relative_path"), async () => {
4608
const bodyValue: string =
4609
'<ri:attachment ri:filename="elephant.png" ri:version-at-save="1" />';
4610
const filePaths: string[] = [
4611
"images/elephant.png",
4612
"parent/inner-parent/elephant.png",
4613
];
4614
const path = "parent/inner-parent/hello-world3.xml";
4615
const expected: string[] = ["parent/inner-parent/elephant.png"];
4616
check(expected, bodyValue, filePaths, path);
4617
});
4618
4619
test(suiteLabel("single_image_lookup_relative_path_win"), async () => {
4620
const bodyValue: string =
4621
'<ri:attachment ri:filename="elephant.png" ri:version-at-save="1" />';
4622
const filePaths: string[] = [
4623
"images\\elephant.png",
4624
"parent\\inner-parent\\elephant.png",
4625
];
4626
const path = "parent\\inner-parent\\hello-world3.xml";
4627
const expected: string[] = ["parent\\inner-parent\\elephant.png"];
4628
check(expected, bodyValue, filePaths, path);
4629
});
4630
4631
test(suiteLabel("single_image_lookup_relative_path_win2"), async () => {
4632
const bodyValue: string =
4633
'<ri:attachment ri:filename="images/elephant.png" ri:version-at-save="1" />';
4634
const filePaths: string[] = ["folder\\images\\elephant.png"];
4635
const path = "folder/index.xml";
4636
const expected: string[] = ["folder\\images\\elephant.png"];
4637
check(expected, bodyValue, filePaths, path);
4638
});
4639
4640
test(suiteLabel("single_image_lookup_dupe_name"), async () => {
4641
const bodyValue: string =
4642
'<ri:attachment ri:filename="elephant.png" ri:version-at-save="1" />';
4643
const filePaths: string[] = [
4644
"fake-path/elephant.png",
4645
"fake-path2/elephant.png",
4646
];
4647
const filePath = "fake-path2/file.xml";
4648
const expected: string[] = ["fake-path2/elephant.png"];
4649
check(expected, bodyValue, filePaths, filePath);
4650
});
4651
4652
test(suiteLabel("single_image_lookup_bad_paths"), async () => {
4653
const bodyValue: string =
4654
'<ri:attachment ri:filename="elephant.png" ri:version-at-save="1" />';
4655
const filePaths: string[] = [
4656
"fake-path-fail/elephant.png",
4657
"fake-path2-fail/elephant.png",
4658
];
4659
const filePath = "fake-path2/file.xml";
4660
const expected: string[] = ["elephant.png"];
4661
check(expected, bodyValue, filePaths, filePath);
4662
});
4663
4664
test(suiteLabel("two_images"), async () => {
4665
const bodyValue: string =
4666
'<ri:attachment ri:filename="fake-image.png" ri:version-at-save="1" /> <ri:attachment ri:filename="fake-image2.png" ri:version-at-save="1" />';
4667
const expected: string[] = ["fake-image.png", "fake-image2.png"];
4668
check(expected, bodyValue);
4669
});
4670
4671
test(suiteLabel("two_images_with_dupes"), async () => {
4672
const bodyValue: string =
4673
'<ri:attachment ri:filename="fake-image.png" ri:version-at-save="1" /> <ri:attachment ri:filename="fake-image.png" ri:version-at-save="1" /> <ri:attachment ri:filename="fake-image2.png" ri:version-at-save="1" />';
4674
const expected: string[] = ["fake-image.png", "fake-image2.png"];
4675
check(expected, bodyValue);
4676
});
4677
4678
test(suiteLabel("image_not_attachment"), async () => {
4679
const bodyValue: string = '"elephant.png"';
4680
const expected: string[] = [];
4681
check(expected, bodyValue);
4682
});
4683
4684
test(suiteLabel("two_images_with_dupes_invalids"), async () => {
4685
const bodyValue: string =
4686
'<ri:attachment ri:filename="fake-image.png" ri:version-at-save="1" /> <ri:attachment ri:filename="fake-image.png" ri:version-at-save="1" /> <ri:attachment ri:filename="fake-image2.png" ri:version-at-save="1" /> no-match.png "no-match.png"';
4687
const expected: string[] = ["fake-image.png", "fake-image2.png"];
4688
check(expected, bodyValue);
4689
});
4690
4691
test(suiteLabel("audio_file"), async () => {
4692
const DOUBLE_BRACKET = "]]";
4693
const bodyValue: string = `<ac:link><ri:attachment ri:filename="audio/2022-11-10-intro-psychological-safety.m4a"/><ac:plain-text-link-body><![CDATA[audio/2022-11-10-intro-psychological-safety.m4a${DOUBLE_BRACKET}></ac:plain-text-link-body></ac:link>`;
4694
const expected: string[] = [
4695
"audio/2022-11-10-intro-psychological-safety.m4a",
4696
];
4697
check(expected, bodyValue);
4698
});
4699
4700
test(suiteLabel("svg_attachment"), async () => {
4701
//5815-bug-confluence-links-to-file-attachments-not-supported
4702
const DOUBLE_BRACKET = "]]";
4703
const bodyValue: string = `<ac:link><ri:attachment ri:filename="quarto-hex.svg"/><ac:plain-text-link-body><![CDATA[quarto-hex-svg${DOUBLE_BRACKET}></ac:plain-text-link-body></ac:link>`;
4704
const expected: string[] = [
4705
"quarto-hex.svg",
4706
];
4707
check(expected, bodyValue);
4708
});
4709
4710
test(suiteLabel("ai_attachment"), async () => {
4711
//5815-bug-confluence-links-to-file-attachments-not-supported
4712
const DOUBLE_BRACKET = "]]";
4713
const bodyValue: string = `<ac:link><ri:attachment ri:filename="quarto-hex.ai"/><ac:plain-text-link-body><![CDATA[quarto-hex-svg${DOUBLE_BRACKET}></ac:plain-text-link-body></ac:link>`;
4714
const expected: string[] = [
4715
"quarto-hex.ai",
4716
];
4717
check(expected, bodyValue);
4718
});
4719
4720
test(suiteLabel("pdf_attachment"), async () => {
4721
//5815-bug-confluence-links-to-file-attachments-not-supported
4722
const DOUBLE_BRACKET = "]]";
4723
const bodyValue: string = `<ac:link><ri:attachment ri:filename="quarto-hex.pdf"/><ac:plain-text-link-body><![CDATA[quarto-hex-svg${DOUBLE_BRACKET}></ac:plain-text-link-body></ac:link>`;
4724
const expected: string[] = [
4725
"quarto-hex.pdf",
4726
];
4727
check(expected, bodyValue);
4728
});
4729
};
4730
4731
const runUpdateImagePathsForContentBody = () => {
4732
const suiteLabel = (label: string) =>
4733
`UpdateImagePathsForContentBody_${label}`;
4734
4735
const UPDATE_NO_IMAGES: ContentBody = {
4736
storage: {
4737
value: "no-images",
4738
representation: "raw",
4739
},
4740
};
4741
4742
const UPDATE_ONE_FLAT_IMAGE: ContentBody = {
4743
storage: {
4744
value:
4745
'<ri:attachment ri:filename="elephant.png" ri:version-at-save="1" />',
4746
representation: "raw",
4747
},
4748
};
4749
const UPDATE_ONE_TO_FLATTEN_IMAGE: ContentBody = {
4750
storage: {
4751
value:
4752
'<ri:attachment ri:filename="a/b/c/elephant.png" ri:version-at-save="1" />',
4753
representation: "raw",
4754
},
4755
};
4756
4757
const check = (expected: ContentBody, bodyValue: ContentBody) => {
4758
assertEquals(expected, updateImagePaths(bodyValue));
4759
};
4760
4761
test(suiteLabel("no_images"), async () => {
4762
const changes = UPDATE_NO_IMAGES;
4763
const expected = UPDATE_NO_IMAGES;
4764
check(expected, changes);
4765
});
4766
4767
test(suiteLabel("images-already-flattened"), async () => {
4768
const changes = UPDATE_ONE_FLAT_IMAGE;
4769
const expected = UPDATE_ONE_FLAT_IMAGE;
4770
check(expected, changes);
4771
});
4772
4773
test(suiteLabel("images-to-flatten"), async () => {
4774
const changes = UPDATE_ONE_TO_FLATTEN_IMAGE;
4775
const expected = UPDATE_ONE_FLAT_IMAGE;
4776
check(expected, changes);
4777
});
4778
};
4779
4780
const runCapFirstLetter = () => {
4781
const suiteLabel = (label: string) => `CapFirstLetter_${label}`;
4782
test(suiteLabel("basic"), async () => {
4783
assertEquals("A", capitalizeFirstLetter("a"));
4784
});
4785
4786
test(suiteLabel("basic_space"), async () => {
4787
assertEquals("A b", capitalizeFirstLetter("a b"));
4788
});
4789
4790
test(suiteLabel("empty"), async () => {
4791
assertEquals("", capitalizeFirstLetter(""));
4792
});
4793
4794
test(suiteLabel("empty2"), async () => {
4795
assertEquals("", capitalizeFirstLetter());
4796
});
4797
};
4798
4799
const runFootnoteTransform = () => {
4800
const suiteLabel = (label: string) => `RunFootnoteTransform_${label}`;
4801
4802
test(suiteLabel("no_note"), async () => {
4803
const value = "<span>No Footnotes</span>";
4804
assertEquals(value, footnoteTransform(value));
4805
});
4806
4807
test(suiteLabel("one_note_forward"), async () => {
4808
const value =
4809
'<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>';
4810
4811
const expected =
4812
'<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fnref1</ac:parameter></ac:structured-macro><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>';
4813
const actual = footnoteTransform(value);
4814
4815
assertEquals(expected, footnoteTransform(value));
4816
});
4817
4818
test(suiteLabel("ignore_existing"), async () => {
4819
const value =
4820
'<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fnref1</ac:parameter></ac:structured-macro><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>' +
4821
'<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">' +
4822
"<hr />" +
4823
"<ol>" +
4824
'<li id="fn1"><p>Here is the footnote.<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fn1</ac:parameter></ac:structured-macro><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>' +
4825
"</ol>" +
4826
"</section>";
4827
const actual = footnoteTransform(value);
4828
assertEquals(value, footnoteTransform(value));
4829
});
4830
4831
test(suiteLabel("one_note_forward_back"), async () => {
4832
const value =
4833
'<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>\n' +
4834
'<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">\n' +
4835
"<hr />\n" +
4836
"<ol>\n" +
4837
'<li id="fn1"><p>Here is the footnote.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>\n' +
4838
"</ol>\n" +
4839
"</section>";
4840
4841
const expected =
4842
'<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fnref1</ac:parameter></ac:structured-macro><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>\n' +
4843
'<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">\n' +
4844
"<hr />\n" +
4845
"<ol>\n" +
4846
'<li id="fn1"><p>Here is the footnote.<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fn1</ac:parameter></ac:structured-macro><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>\n' +
4847
"</ol>\n" +
4848
"</section>";
4849
const actual = footnoteTransform(value);
4850
assertEquals(expected, actual);
4851
});
4852
4853
test(suiteLabel("two_notes"), async () => {
4854
const value =
4855
'<p>Here is a footnote reference,<a href="#fn1" class="footnote-ref"><sup>1</sup></a> and another.<a href="#fn2" class="footnote-ref"><sup>2</sup></a></p>\n' +
4856
"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam dapibus mattis malesuada. Sed fringilla posuere ultricies. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur scelerisque, nisi a consequat aliquet, metus augue feugiat augue, non eleifend lectus eros non nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Mauris sollicitudin auctor orci id vulputate. Pellentesque at luctus urna. Quisque pretium sapien at elit congue maximus. Mauris fermentum sapien eget justo elementum, eleifend auctor dui mattis. Morbi purus elit, auctor id magna et, blandit mattis nunc. Cras commodo leo ut ultrices semper. Nunc a libero dapibus, vestibulum velit sed, bibendum risus. Duis sollicitudin, libero ac sollicitudin maximus, ante massa blandit risus, non laoreet lectus justo non nisl.</p>\n" +
4857
"<hr />\n" +
4858
"<ol>\n" +
4859
" <li>\n" +
4860
' <p>Here is the footnote.<a href="#fnref1" class="footnote-back">↩︎</a></p>\n' +
4861
" </li>\n" +
4862
" <li>\n" +
4863
" <p>Here&rsquo;s one with multiple blocks.</p>\n" +
4864
' <p>The whole paragraph can be indented, or just the first line. In this way, multi-paragraph footnotes work like multi-paragraph list items.<a href="#fnref2" class="footnote-back">↩︎</a></p>\n' +
4865
" </li>\n" +
4866
"</ol>";
4867
4868
const expected =
4869
'<p>Here is a footnote reference,<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fnref1</ac:parameter></ac:structured-macro><a href="#fn1" class="footnote-ref"><sup>1</sup></a> and another.<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fnref2</ac:parameter></ac:structured-macro><a href="#fn2" class="footnote-ref"><sup>2</sup></a></p>\n' +
4870
"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam dapibus mattis malesuada. Sed fringilla posuere ultricies. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur scelerisque, nisi a consequat aliquet, metus augue feugiat augue, non eleifend lectus eros non nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Mauris sollicitudin auctor orci id vulputate. Pellentesque at luctus urna. Quisque pretium sapien at elit congue maximus. Mauris fermentum sapien eget justo elementum, eleifend auctor dui mattis. Morbi purus elit, auctor id magna et, blandit mattis nunc. Cras commodo leo ut ultrices semper. Nunc a libero dapibus, vestibulum velit sed, bibendum risus. Duis sollicitudin, libero ac sollicitudin maximus, ante massa blandit risus, non laoreet lectus justo non nisl.</p>\n" +
4871
"<hr />\n" +
4872
"<ol>\n" +
4873
" <li>\n" +
4874
' <p>Here is the footnote.<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fn1</ac:parameter></ac:structured-macro><a href="#fnref1" class="footnote-back">↩︎</a></p>\n' +
4875
" </li>\n" +
4876
" <li>\n" +
4877
" <p>Here&rsquo;s one with multiple blocks.</p>\n" +
4878
' <p>The whole paragraph can be indented, or just the first line. In this way, multi-paragraph footnotes work like multi-paragraph list items.<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:local-id="a6aa6f25-0bee-4a7f-929b-71fcb7eba592" ac:macro-id="d2cb5be1217ae6e086bc60005e9d27b7"><ac:parameter ac:name="">fn2</ac:parameter></ac:structured-macro><a href="#fnref2" class="footnote-back">↩︎</a></p>\n' +
4879
" </li>\n" +
4880
"</ol>";
4881
const actual = footnoteTransform(value);
4882
assertEquals(expected, actual);
4883
});
4884
};
4885
4886
if (HIDE_NOISE) {
4887
console.info(
4888
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
4889
);
4890
}
4891
4892
if (RUN_ALL_TESTS) {
4893
runGeneralTests();
4894
runFilterFilesForUpdate();
4895
runBuildSpaceChanges();
4896
runSpaceCreatesWithNesting();
4897
runSpaceUpdatesWithNesting();
4898
runMergeSitePages();
4899
runPublishRecordTests();
4900
runGetNextVersionTests();
4901
runWriteTokenComparator();
4902
runBuildContentCreate();
4903
runGetTitle();
4904
runBuildFileToMetaTable();
4905
runExtractLinks();
4906
runUpdateLinks();
4907
runConvertForSecondPass();
4908
runFindAttachments();
4909
runUpdateImagePathsForContentBody();
4910
runCapFirstLetter();
4911
runFootnoteTransform();
4912
runConfluenceParentFromString();
4913
runFlattenIndexes();
4914
} else {
4915
runFindAttachments();
4916
}
4917
4918