@covalent/markdown 0.0.0-COVALENT
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.browserslistrc +16 -0
- package/.eslintrc.json +36 -0
- package/README.md +103 -0
- package/_markdown-theme.scss +72 -0
- package/jest.config.js +23 -0
- package/ng-package.json +5 -0
- package/package.json +34 -0
- package/project.json +64 -0
- package/src/lib/markdown-loader/markdown-loader.service.spec.ts +101 -0
- package/src/lib/markdown-loader/markdown-loader.service.ts +35 -0
- package/src/lib/markdown-utils/markdown-utils.spec.ts +189 -0
- package/src/lib/markdown-utils/markdown-utils.ts +148 -0
- package/src/lib/markdown.component.html +1 -0
- package/src/lib/markdown.component.scss +664 -0
- package/src/lib/markdown.component.spec.ts +786 -0
- package/src/lib/markdown.component.ts +471 -0
- package/src/lib/markdown.module.ts +23 -0
- package/src/public_api.ts +4 -0
- package/src/test-setup.ts +1 -0
- package/tailwind.config.js +13 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +12 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +10 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TestBed,
|
|
3
|
+
waitForAsync,
|
|
4
|
+
ComponentFixture,
|
|
5
|
+
tick,
|
|
6
|
+
fakeAsync,
|
|
7
|
+
} from '@angular/core/testing';
|
|
8
|
+
import { ApplicationRef, Component } from '@angular/core';
|
|
9
|
+
import { By } from '@angular/platform-browser';
|
|
10
|
+
import { CovalentMarkdownModule } from './markdown.module';
|
|
11
|
+
import { TdMarkdownComponent } from './markdown.component';
|
|
12
|
+
|
|
13
|
+
// Implementing scrollIntoView since its not implemented JSDOM
|
|
14
|
+
window.HTMLElement.prototype.scrollIntoView = function () {
|
|
15
|
+
// noop
|
|
16
|
+
};
|
|
17
|
+
window.scrollTo = function () {
|
|
18
|
+
// noop
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function anchorTestMarkdown(): string {
|
|
22
|
+
const heading1 = 'Heading 1';
|
|
23
|
+
const heading2 = 'Heading 2';
|
|
24
|
+
let str = `* **[${heading1}](#heading-1)** \n * **[${heading2}](#heading-2)** \n`;
|
|
25
|
+
const arr: number[] = Array(100).fill(0);
|
|
26
|
+
arr.forEach(() => {
|
|
27
|
+
str += '\n * item \n';
|
|
28
|
+
});
|
|
29
|
+
str += `\n # ${heading1} \n`;
|
|
30
|
+
arr.forEach(() => {
|
|
31
|
+
str += '\n * item \n';
|
|
32
|
+
});
|
|
33
|
+
str += `\n # ${heading2} \n`;
|
|
34
|
+
return str;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function anchorTestNonEnglishMarkdown(): string {
|
|
38
|
+
const heading1 = 'L10Nテスト';
|
|
39
|
+
const heading2 = 'すべてが楽しいです!';
|
|
40
|
+
const heading3 = '誰もが一生に一度ローカライズする必要があります。';
|
|
41
|
+
let str = `* **[${heading1}](#${heading1})** \n * **[${heading2}](#${heading2})** \n * **[${heading3}](#${heading3})** \n`;
|
|
42
|
+
const arr: number[] = Array(100).fill(0);
|
|
43
|
+
arr.forEach(() => {
|
|
44
|
+
str += '\n * item \n';
|
|
45
|
+
});
|
|
46
|
+
str += `\n # ${heading1} \n`;
|
|
47
|
+
arr.forEach(() => {
|
|
48
|
+
str += '\n * item \n';
|
|
49
|
+
});
|
|
50
|
+
str += `\n # ${heading2} \n`;
|
|
51
|
+
arr.forEach(() => {
|
|
52
|
+
str += '\n * item \n';
|
|
53
|
+
});
|
|
54
|
+
str += `\n # ${heading3} \n`;
|
|
55
|
+
return str;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe('Component: Markdown', () => {
|
|
59
|
+
beforeEach(waitForAsync(() => {
|
|
60
|
+
TestBed.configureTestingModule({
|
|
61
|
+
imports: [
|
|
62
|
+
TdMarkdownEmptyStaticContentTestRenderingComponent,
|
|
63
|
+
TdMarkdownStaticContentTestRenderingComponent,
|
|
64
|
+
TdMarkdownDymanicContentTestRenderingComponent,
|
|
65
|
+
TdMarkdownSimpleLineBreaksTestRenderingComponent,
|
|
66
|
+
|
|
67
|
+
TdMarkdownEmptyStaticContentTestEventsComponent,
|
|
68
|
+
TdMarkdownStaticContentTestEventsComponent,
|
|
69
|
+
TdMarkdownDynamicContentTestEventsComponent,
|
|
70
|
+
TdMarkdownAnchorsTestEventsComponent,
|
|
71
|
+
TdMarkdownLinksTestEventsComponent,
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
TestBed.compileComponents();
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
describe('Rendering: ', () => {
|
|
78
|
+
it('should render empty static content', waitForAsync(() => {
|
|
79
|
+
const fixture: ComponentFixture<TdMarkdownEmptyStaticContentTestRenderingComponent> =
|
|
80
|
+
TestBed.createComponent(
|
|
81
|
+
TdMarkdownEmptyStaticContentTestRenderingComponent
|
|
82
|
+
);
|
|
83
|
+
const element: HTMLElement = fixture.nativeElement;
|
|
84
|
+
|
|
85
|
+
expect(
|
|
86
|
+
fixture.debugElement
|
|
87
|
+
.query(By.css('td-markdown'))
|
|
88
|
+
.nativeElement.textContent.trim()
|
|
89
|
+
).toBe(``);
|
|
90
|
+
expect(element.querySelector('td-markdown div')).toBeFalsy();
|
|
91
|
+
fixture.detectChanges();
|
|
92
|
+
fixture.whenStable().then(() => {
|
|
93
|
+
fixture.detectChanges();
|
|
94
|
+
expect(element.querySelector('td-markdown div')).toBeFalsy();
|
|
95
|
+
expect(
|
|
96
|
+
fixture.debugElement
|
|
97
|
+
.query(By.css('td-markdown'))
|
|
98
|
+
.nativeElement.textContent.trim()
|
|
99
|
+
).toBe('');
|
|
100
|
+
});
|
|
101
|
+
}));
|
|
102
|
+
|
|
103
|
+
it('should render markup from static content', waitForAsync(() => {
|
|
104
|
+
const fixture: ComponentFixture<TdMarkdownStaticContentTestRenderingComponent> =
|
|
105
|
+
TestBed.createComponent(TdMarkdownStaticContentTestRenderingComponent);
|
|
106
|
+
const element: HTMLElement = fixture.nativeElement;
|
|
107
|
+
|
|
108
|
+
expect(
|
|
109
|
+
fixture.debugElement
|
|
110
|
+
.query(By.css('td-markdown'))
|
|
111
|
+
.nativeElement.textContent.trim()
|
|
112
|
+
).toBe(
|
|
113
|
+
`
|
|
114
|
+
# title
|
|
115
|
+
|
|
116
|
+
* list item`.trim()
|
|
117
|
+
);
|
|
118
|
+
expect(element.querySelector('td-markdown div')).toBeFalsy();
|
|
119
|
+
fixture.detectChanges();
|
|
120
|
+
fixture.whenStable().then(() => {
|
|
121
|
+
fixture.detectChanges();
|
|
122
|
+
expect(element.querySelector('td-markdown div')).toBeTruthy();
|
|
123
|
+
expect(
|
|
124
|
+
element.querySelector('td-markdown div h1')?.textContent?.trim()
|
|
125
|
+
).toBe('title');
|
|
126
|
+
expect(
|
|
127
|
+
element.querySelector('td-markdown div ul li')?.textContent?.trim()
|
|
128
|
+
).toBe('list item');
|
|
129
|
+
});
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
it('should render newlines as <br/> if simpleLineBreaks is true', waitForAsync(() => {
|
|
133
|
+
const fixture: ComponentFixture<TdMarkdownSimpleLineBreaksTestRenderingComponent> =
|
|
134
|
+
TestBed.createComponent(
|
|
135
|
+
TdMarkdownSimpleLineBreaksTestRenderingComponent
|
|
136
|
+
);
|
|
137
|
+
const component: TdMarkdownSimpleLineBreaksTestRenderingComponent =
|
|
138
|
+
fixture.debugElement.componentInstance;
|
|
139
|
+
component.simpleLineBreaks = true;
|
|
140
|
+
const element: HTMLElement = fixture.nativeElement;
|
|
141
|
+
|
|
142
|
+
expect(
|
|
143
|
+
fixture.debugElement
|
|
144
|
+
.query(By.css('td-markdown'))
|
|
145
|
+
.nativeElement.textContent.trim()
|
|
146
|
+
).toBe(
|
|
147
|
+
`
|
|
148
|
+
first line
|
|
149
|
+
second line
|
|
150
|
+
third line
|
|
151
|
+
`.trim()
|
|
152
|
+
);
|
|
153
|
+
expect(element.querySelector('td-markdown div')).toBeFalsy();
|
|
154
|
+
fixture.detectChanges();
|
|
155
|
+
fixture.whenStable().then(() => {
|
|
156
|
+
fixture.detectChanges();
|
|
157
|
+
fixture.whenStable().then(() => {
|
|
158
|
+
fixture.detectChanges();
|
|
159
|
+
expect(element.querySelector('td-markdown div')).toBeTruthy();
|
|
160
|
+
expect(
|
|
161
|
+
element.querySelector('td-markdown')?.querySelectorAll('br').length
|
|
162
|
+
).toBe(2);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
it('should not render newlines as <br/> if simpleLineBreaks is false', waitForAsync(() => {
|
|
168
|
+
const fixture: ComponentFixture<TdMarkdownSimpleLineBreaksTestRenderingComponent> =
|
|
169
|
+
TestBed.createComponent(
|
|
170
|
+
TdMarkdownSimpleLineBreaksTestRenderingComponent
|
|
171
|
+
);
|
|
172
|
+
const component: TdMarkdownSimpleLineBreaksTestRenderingComponent =
|
|
173
|
+
fixture.debugElement.componentInstance;
|
|
174
|
+
component.simpleLineBreaks = false;
|
|
175
|
+
const element: HTMLElement = fixture.nativeElement;
|
|
176
|
+
|
|
177
|
+
expect(
|
|
178
|
+
fixture.debugElement
|
|
179
|
+
.query(By.css('td-markdown'))
|
|
180
|
+
.nativeElement.textContent.trim()
|
|
181
|
+
).toBe(
|
|
182
|
+
`
|
|
183
|
+
first line
|
|
184
|
+
second line
|
|
185
|
+
third line
|
|
186
|
+
`.trim()
|
|
187
|
+
);
|
|
188
|
+
expect(element.querySelector('td-markdown div')).toBeFalsy();
|
|
189
|
+
fixture.detectChanges();
|
|
190
|
+
fixture.whenStable().then(() => {
|
|
191
|
+
fixture.detectChanges();
|
|
192
|
+
fixture.whenStable().then(() => {
|
|
193
|
+
fixture.detectChanges();
|
|
194
|
+
expect(element.querySelector('td-markdown div')).toBeTruthy();
|
|
195
|
+
expect(
|
|
196
|
+
element.querySelector('td-markdown')?.querySelectorAll('br').length
|
|
197
|
+
).toBe(0);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}));
|
|
201
|
+
|
|
202
|
+
it('should render markup from dynamic content', waitForAsync(() => {
|
|
203
|
+
const fixture: ComponentFixture<TdMarkdownDymanicContentTestRenderingComponent> =
|
|
204
|
+
TestBed.createComponent(TdMarkdownDymanicContentTestRenderingComponent);
|
|
205
|
+
const component: TdMarkdownDymanicContentTestRenderingComponent =
|
|
206
|
+
fixture.debugElement.componentInstance;
|
|
207
|
+
component.content = `
|
|
208
|
+
# another title
|
|
209
|
+
|
|
210
|
+
## subtitle
|
|
211
|
+
|
|
212
|
+
\`\`\`
|
|
213
|
+
pseudo code
|
|
214
|
+
\`\`\``;
|
|
215
|
+
const element: HTMLElement = fixture.nativeElement;
|
|
216
|
+
|
|
217
|
+
expect(
|
|
218
|
+
fixture.debugElement
|
|
219
|
+
.query(By.css('td-markdown'))
|
|
220
|
+
.nativeElement.textContent.trim()
|
|
221
|
+
).toBe('');
|
|
222
|
+
expect(element.querySelector('td-markdown div')).toBeFalsy();
|
|
223
|
+
fixture.detectChanges();
|
|
224
|
+
fixture.whenStable().then(() => {
|
|
225
|
+
fixture.detectChanges();
|
|
226
|
+
expect(element.querySelector('td-markdown div')).toBeTruthy();
|
|
227
|
+
expect(
|
|
228
|
+
element.querySelector('td-markdown div h1')?.textContent?.trim()
|
|
229
|
+
).toBe('another title');
|
|
230
|
+
expect(
|
|
231
|
+
element.querySelector('td-markdown div h2')?.textContent?.trim()
|
|
232
|
+
).toBe('subtitle');
|
|
233
|
+
expect(
|
|
234
|
+
element.querySelector('td-markdown div code')?.textContent?.trim()
|
|
235
|
+
).toBe('pseudo code');
|
|
236
|
+
});
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
it('should render markup from dynamic content incorrectly', waitForAsync(() => {
|
|
240
|
+
const fixture: ComponentFixture<TdMarkdownDymanicContentTestRenderingComponent> =
|
|
241
|
+
TestBed.createComponent(TdMarkdownDymanicContentTestRenderingComponent);
|
|
242
|
+
const component: TdMarkdownDymanicContentTestRenderingComponent =
|
|
243
|
+
fixture.debugElement.componentInstance;
|
|
244
|
+
component.content = `
|
|
245
|
+
# another title
|
|
246
|
+
|
|
247
|
+
## subtitle`;
|
|
248
|
+
const element: HTMLElement = fixture.nativeElement;
|
|
249
|
+
|
|
250
|
+
expect(
|
|
251
|
+
fixture.debugElement
|
|
252
|
+
.query(By.css('td-markdown'))
|
|
253
|
+
.nativeElement.textContent.trim()
|
|
254
|
+
).toBe('');
|
|
255
|
+
expect(element.querySelector('td-markdown div')).toBeFalsy();
|
|
256
|
+
fixture.detectChanges();
|
|
257
|
+
fixture.whenStable().then(() => {
|
|
258
|
+
fixture.detectChanges();
|
|
259
|
+
expect(element.querySelector('td-markdown div')).toBeTruthy();
|
|
260
|
+
expect(
|
|
261
|
+
element.querySelector('td-markdown div h1')?.textContent?.trim()
|
|
262
|
+
).toBe('another title');
|
|
263
|
+
expect(element.querySelector('td-markdown div h2')).toBeFalsy();
|
|
264
|
+
expect(
|
|
265
|
+
element.querySelector('td-markdown div')?.textContent?.trim()
|
|
266
|
+
).toContain('## subtitle');
|
|
267
|
+
});
|
|
268
|
+
}));
|
|
269
|
+
|
|
270
|
+
it('should jump to anchor when anchor input is changed', waitForAsync(async () => {
|
|
271
|
+
const fixture: ComponentFixture<TdMarkdownAnchorsTestEventsComponent> =
|
|
272
|
+
TestBed.createComponent(TdMarkdownAnchorsTestEventsComponent);
|
|
273
|
+
const component: TdMarkdownAnchorsTestEventsComponent =
|
|
274
|
+
fixture.debugElement.componentInstance;
|
|
275
|
+
component.content = anchorTestMarkdown();
|
|
276
|
+
|
|
277
|
+
fixture.detectChanges();
|
|
278
|
+
await fixture.whenStable();
|
|
279
|
+
|
|
280
|
+
window.scrollTo(0, 0);
|
|
281
|
+
const originalScrollPos: number = window.scrollY;
|
|
282
|
+
component.anchor = 'heading 1';
|
|
283
|
+
|
|
284
|
+
fixture.detectChanges();
|
|
285
|
+
await fixture.whenStable();
|
|
286
|
+
|
|
287
|
+
const heading1ScrollPos: number = window.scrollY;
|
|
288
|
+
expect(heading1ScrollPos).toBeGreaterThanOrEqual(originalScrollPos);
|
|
289
|
+
|
|
290
|
+
component.anchor = 'heading 2';
|
|
291
|
+
|
|
292
|
+
fixture.detectChanges();
|
|
293
|
+
await fixture.whenStable();
|
|
294
|
+
|
|
295
|
+
const heading2ScrollPos: number = window.scrollY;
|
|
296
|
+
expect(heading2ScrollPos).toBeGreaterThanOrEqual(heading1ScrollPos);
|
|
297
|
+
|
|
298
|
+
component.anchor = 'heading 1';
|
|
299
|
+
|
|
300
|
+
fixture.detectChanges();
|
|
301
|
+
await fixture.whenStable();
|
|
302
|
+
|
|
303
|
+
expect(window.scrollY).toBeLessThanOrEqual(heading2ScrollPos);
|
|
304
|
+
}));
|
|
305
|
+
|
|
306
|
+
it('should jump to anchor if an anchor link is clicked', waitForAsync(async () => {
|
|
307
|
+
const fixture: ComponentFixture<TdMarkdownAnchorsTestEventsComponent> =
|
|
308
|
+
TestBed.createComponent(TdMarkdownAnchorsTestEventsComponent);
|
|
309
|
+
const component: TdMarkdownAnchorsTestEventsComponent =
|
|
310
|
+
fixture.debugElement.componentInstance;
|
|
311
|
+
component.content = anchorTestMarkdown();
|
|
312
|
+
|
|
313
|
+
fixture.detectChanges();
|
|
314
|
+
await fixture.whenStable();
|
|
315
|
+
|
|
316
|
+
window.scrollTo(0, 0);
|
|
317
|
+
const originalScrollPos: number = window.scrollY;
|
|
318
|
+
const headings: HTMLElement[] =
|
|
319
|
+
fixture.debugElement.nativeElement.querySelectorAll('a');
|
|
320
|
+
const heading1: HTMLElement = headings[0];
|
|
321
|
+
const heading2: HTMLElement = headings[1];
|
|
322
|
+
heading1.click();
|
|
323
|
+
|
|
324
|
+
fixture.detectChanges();
|
|
325
|
+
await fixture.whenStable();
|
|
326
|
+
|
|
327
|
+
const heading1ScrollPos: number = window.scrollY;
|
|
328
|
+
expect(heading1ScrollPos).toBeGreaterThanOrEqual(originalScrollPos);
|
|
329
|
+
|
|
330
|
+
heading2.click();
|
|
331
|
+
|
|
332
|
+
fixture.detectChanges();
|
|
333
|
+
await fixture.whenStable();
|
|
334
|
+
|
|
335
|
+
const heading2ScrollPos: number = window.scrollY;
|
|
336
|
+
expect(heading2ScrollPos).toBeGreaterThanOrEqual(heading1ScrollPos);
|
|
337
|
+
|
|
338
|
+
heading1.click();
|
|
339
|
+
|
|
340
|
+
fixture.detectChanges();
|
|
341
|
+
await fixture.whenStable();
|
|
342
|
+
|
|
343
|
+
expect(window.scrollY).toBeLessThanOrEqual(heading2ScrollPos);
|
|
344
|
+
}));
|
|
345
|
+
|
|
346
|
+
it('should jump to anchor if an anchor link is clicked but should not run change detection', async () => {
|
|
347
|
+
const fixture: ComponentFixture<TdMarkdownAnchorsTestEventsComponent> =
|
|
348
|
+
TestBed.createComponent(TdMarkdownAnchorsTestEventsComponent);
|
|
349
|
+
const component: TdMarkdownAnchorsTestEventsComponent =
|
|
350
|
+
fixture.debugElement.componentInstance;
|
|
351
|
+
component.content = anchorTestMarkdown();
|
|
352
|
+
|
|
353
|
+
fixture.detectChanges();
|
|
354
|
+
await fixture.whenStable();
|
|
355
|
+
|
|
356
|
+
const appRef: ApplicationRef = TestBed.inject(ApplicationRef);
|
|
357
|
+
jest.spyOn(appRef, 'tick');
|
|
358
|
+
|
|
359
|
+
window.scrollTo(0, 0);
|
|
360
|
+
const originalScrollPos: number = window.scrollY;
|
|
361
|
+
const heading: HTMLElement =
|
|
362
|
+
fixture.debugElement.nativeElement.querySelector('a');
|
|
363
|
+
|
|
364
|
+
const event: Event = new Event('click', { bubbles: true });
|
|
365
|
+
jest.spyOn(event, 'preventDefault');
|
|
366
|
+
|
|
367
|
+
heading.dispatchEvent(event);
|
|
368
|
+
|
|
369
|
+
const headingScrollPos: number = window.scrollY;
|
|
370
|
+
|
|
371
|
+
expect(appRef.tick).not.toHaveBeenCalled();
|
|
372
|
+
expect(event.preventDefault).toHaveBeenCalled();
|
|
373
|
+
expect(headingScrollPos).toBeGreaterThanOrEqual(originalScrollPos);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should emit `contentReady` and should not run change detection', fakeAsync(() => {
|
|
377
|
+
const contentReadySpy: jest.Mock = jest.fn();
|
|
378
|
+
const fixture: ComponentFixture<TdMarkdownAnchorsTestEventsComponent> =
|
|
379
|
+
TestBed.createComponent(TdMarkdownAnchorsTestEventsComponent);
|
|
380
|
+
const component: TdMarkdownAnchorsTestEventsComponent =
|
|
381
|
+
fixture.debugElement.componentInstance;
|
|
382
|
+
component.anchor = 'heading 1';
|
|
383
|
+
component.content = anchorTestMarkdown();
|
|
384
|
+
|
|
385
|
+
const { componentInstance } = fixture.debugElement.query(
|
|
386
|
+
By.directive(TdMarkdownComponent)
|
|
387
|
+
);
|
|
388
|
+
componentInstance.contentReady.subscribe(contentReadySpy);
|
|
389
|
+
|
|
390
|
+
fixture.detectChanges();
|
|
391
|
+
|
|
392
|
+
const appRef: ApplicationRef = TestBed.inject(ApplicationRef);
|
|
393
|
+
jest.spyOn(appRef, 'tick');
|
|
394
|
+
|
|
395
|
+
tick(250);
|
|
396
|
+
|
|
397
|
+
expect(appRef.tick).not.toHaveBeenCalled();
|
|
398
|
+
expect(contentReadySpy).toHaveBeenCalled();
|
|
399
|
+
}));
|
|
400
|
+
|
|
401
|
+
it('should jump to anchor if an anchor link is clicked regardless of lang', waitForAsync(async () => {
|
|
402
|
+
const fixture: ComponentFixture<TdMarkdownAnchorsTestEventsComponent> =
|
|
403
|
+
TestBed.createComponent(TdMarkdownAnchorsTestEventsComponent);
|
|
404
|
+
const component: TdMarkdownAnchorsTestEventsComponent =
|
|
405
|
+
fixture.debugElement.componentInstance;
|
|
406
|
+
component.content = anchorTestNonEnglishMarkdown();
|
|
407
|
+
|
|
408
|
+
fixture.detectChanges();
|
|
409
|
+
await fixture.whenStable();
|
|
410
|
+
|
|
411
|
+
window.scrollTo(0, 0);
|
|
412
|
+
const originalScrollPos: number = window.scrollY;
|
|
413
|
+
const headings: HTMLElement[] =
|
|
414
|
+
fixture.debugElement.nativeElement.querySelectorAll('a');
|
|
415
|
+
const heading1: HTMLElement = headings[0];
|
|
416
|
+
const heading2: HTMLElement = headings[1];
|
|
417
|
+
heading1.click();
|
|
418
|
+
|
|
419
|
+
fixture.detectChanges();
|
|
420
|
+
await fixture.whenStable();
|
|
421
|
+
|
|
422
|
+
const heading1ScrollPos: number = window.scrollY;
|
|
423
|
+
expect(heading1ScrollPos).toBeGreaterThanOrEqual(originalScrollPos);
|
|
424
|
+
|
|
425
|
+
heading2.click();
|
|
426
|
+
|
|
427
|
+
fixture.detectChanges();
|
|
428
|
+
await fixture.whenStable();
|
|
429
|
+
|
|
430
|
+
const heading2ScrollPos: number = window.scrollY;
|
|
431
|
+
expect(heading2ScrollPos).toBeGreaterThanOrEqual(heading1ScrollPos);
|
|
432
|
+
|
|
433
|
+
heading1.click();
|
|
434
|
+
|
|
435
|
+
fixture.detectChanges();
|
|
436
|
+
await fixture.whenStable();
|
|
437
|
+
|
|
438
|
+
expect(window.scrollY).toBeLessThanOrEqual(heading2ScrollPos);
|
|
439
|
+
}));
|
|
440
|
+
|
|
441
|
+
it('should generate the proper urls', waitForAsync(async () => {
|
|
442
|
+
const fixture: ComponentFixture<TdMarkdownLinksTestEventsComponent> =
|
|
443
|
+
TestBed.createComponent(TdMarkdownLinksTestEventsComponent);
|
|
444
|
+
const component: TdMarkdownLinksTestEventsComponent =
|
|
445
|
+
fixture.debugElement.componentInstance;
|
|
446
|
+
|
|
447
|
+
const ANCHOR = '#anchor';
|
|
448
|
+
const CURRENT_MD_FILE = 'GETTING_STARTED.md';
|
|
449
|
+
const ROOT_MD_FILE = 'README.md';
|
|
450
|
+
const NON_RAW_LINK = 'https://github.com/Teradata/covalent/blob/main/';
|
|
451
|
+
const RAW_LINK =
|
|
452
|
+
'https://raw.githubusercontent.com/Teradata/covalent/main/';
|
|
453
|
+
const RELATIVE_LINK = 'assets/covalent/';
|
|
454
|
+
const EXTERNAL_URL = 'https://angular.dev/';
|
|
455
|
+
const SUB_DIRECTORY = 'docs/';
|
|
456
|
+
const links: string[][] = [
|
|
457
|
+
[`${ANCHOR}`, `${ANCHOR}`],
|
|
458
|
+
[`${NON_RAW_LINK}${ROOT_MD_FILE}`, `${NON_RAW_LINK}${ROOT_MD_FILE}`],
|
|
459
|
+
[
|
|
460
|
+
`${NON_RAW_LINK}${ROOT_MD_FILE}${ANCHOR}`,
|
|
461
|
+
`${NON_RAW_LINK}${ROOT_MD_FILE}${ANCHOR}`,
|
|
462
|
+
],
|
|
463
|
+
[`${RAW_LINK}${ROOT_MD_FILE}`, `${RAW_LINK}${ROOT_MD_FILE}`],
|
|
464
|
+
[
|
|
465
|
+
`${RAW_LINK}${ROOT_MD_FILE}${ANCHOR}`,
|
|
466
|
+
`${RAW_LINK}${ROOT_MD_FILE}${ANCHOR}`,
|
|
467
|
+
],
|
|
468
|
+
|
|
469
|
+
[`${EXTERNAL_URL}${ROOT_MD_FILE}`, `${EXTERNAL_URL}${ROOT_MD_FILE}`],
|
|
470
|
+
[
|
|
471
|
+
`${EXTERNAL_URL}${ROOT_MD_FILE}${ANCHOR}`,
|
|
472
|
+
`${EXTERNAL_URL}${ROOT_MD_FILE}${ANCHOR}`,
|
|
473
|
+
],
|
|
474
|
+
[`${EXTERNAL_URL}`, `${EXTERNAL_URL}`],
|
|
475
|
+
[`${EXTERNAL_URL}${ANCHOR}`, `${EXTERNAL_URL}${ANCHOR}`],
|
|
476
|
+
];
|
|
477
|
+
|
|
478
|
+
let markdown = '';
|
|
479
|
+
|
|
480
|
+
links.forEach((link: string[]) => {
|
|
481
|
+
markdown += `* [${link[0]}](${link[0]}) \n`;
|
|
482
|
+
});
|
|
483
|
+
component.content = markdown;
|
|
484
|
+
const anchorElements: HTMLAnchorElement[] =
|
|
485
|
+
fixture.debugElement.nativeElement.querySelectorAll('a');
|
|
486
|
+
function checkAnchors(): void {
|
|
487
|
+
Array.from<HTMLAnchorElement>(anchorElements).forEach(
|
|
488
|
+
(anchorElement: HTMLAnchorElement, index: number) => {
|
|
489
|
+
const href = anchorElement.getAttribute('href');
|
|
490
|
+
const expectedHref: string = links[index][1];
|
|
491
|
+
expect(href).toEqual(expectedHref);
|
|
492
|
+
}
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
component.hostedUrl = `${RAW_LINK}${SUB_DIRECTORY}${CURRENT_MD_FILE}`;
|
|
497
|
+
fixture.detectChanges();
|
|
498
|
+
await fixture.whenStable();
|
|
499
|
+
checkAnchors();
|
|
500
|
+
|
|
501
|
+
component.hostedUrl = `${NON_RAW_LINK}${SUB_DIRECTORY}${CURRENT_MD_FILE}`;
|
|
502
|
+
fixture.detectChanges();
|
|
503
|
+
await fixture.whenStable();
|
|
504
|
+
checkAnchors();
|
|
505
|
+
|
|
506
|
+
component.hostedUrl = `${RELATIVE_LINK}${SUB_DIRECTORY}${CURRENT_MD_FILE}`;
|
|
507
|
+
fixture.detectChanges();
|
|
508
|
+
await fixture.whenStable();
|
|
509
|
+
checkAnchors();
|
|
510
|
+
}));
|
|
511
|
+
|
|
512
|
+
it('should generate the proper image urls', waitForAsync(async () => {
|
|
513
|
+
const fixture: ComponentFixture<TdMarkdownLinksTestEventsComponent> =
|
|
514
|
+
TestBed.createComponent(TdMarkdownLinksTestEventsComponent);
|
|
515
|
+
const component: TdMarkdownLinksTestEventsComponent =
|
|
516
|
+
fixture.debugElement.componentInstance;
|
|
517
|
+
|
|
518
|
+
const CURRENT_MD_FILE = 'readme.md';
|
|
519
|
+
const SIBLING_IMG = 'typescript.jpg';
|
|
520
|
+
const ROOT_IMG = 'angular.png';
|
|
521
|
+
const NON_RAW_LINK = 'https://github.com/Teradata/covalent/blob/main/';
|
|
522
|
+
const RAW_LINK =
|
|
523
|
+
'https://raw.githubusercontent.com/Teradata/covalent/main/';
|
|
524
|
+
const RELATIVE_LINK = 'assets/covalent/';
|
|
525
|
+
const EXTERNAL_IMG =
|
|
526
|
+
'https://angular.dev/assets/images/logos/angular/angular.svg';
|
|
527
|
+
const SUB_DIRECTORY = 'dir/';
|
|
528
|
+
const SVG_IMG = 'src/assets/icons/covalent.svg';
|
|
529
|
+
// these are not valid image urls
|
|
530
|
+
const images: string[][] = [
|
|
531
|
+
[`./${SIBLING_IMG}`, `${RAW_LINK}${SUB_DIRECTORY}${SIBLING_IMG}`],
|
|
532
|
+
[`${SIBLING_IMG}`, `${RAW_LINK}${SUB_DIRECTORY}${SIBLING_IMG}`],
|
|
533
|
+
[`../${ROOT_IMG}`, `${RAW_LINK}${ROOT_IMG}`],
|
|
534
|
+
[`/${ROOT_IMG}`, `${RAW_LINK}${ROOT_IMG}`],
|
|
535
|
+
[`${RAW_LINK}${ROOT_IMG}`, `${RAW_LINK}${ROOT_IMG}`],
|
|
536
|
+
[`${EXTERNAL_IMG}`, `${EXTERNAL_IMG}`],
|
|
537
|
+
[
|
|
538
|
+
`${NON_RAW_LINK}${SUB_DIRECTORY}${SIBLING_IMG}`,
|
|
539
|
+
`${RAW_LINK}${SUB_DIRECTORY}${SIBLING_IMG}`,
|
|
540
|
+
],
|
|
541
|
+
[`${NON_RAW_LINK}${SVG_IMG}`, `${RAW_LINK}${SVG_IMG}?sanitize=true`],
|
|
542
|
+
];
|
|
543
|
+
|
|
544
|
+
let markdown = '';
|
|
545
|
+
|
|
546
|
+
images.forEach((link: string[]) => {
|
|
547
|
+
markdown += `* ![${link[0]}](${link[0]}) \n`;
|
|
548
|
+
});
|
|
549
|
+
component.content = markdown;
|
|
550
|
+
|
|
551
|
+
const imageElements: HTMLImageElement[] =
|
|
552
|
+
fixture.debugElement.nativeElement.querySelectorAll('img');
|
|
553
|
+
function checkImages(): void {
|
|
554
|
+
Array.from(imageElements).forEach(
|
|
555
|
+
(imageElement: HTMLImageElement, index: number) => {
|
|
556
|
+
const src = imageElement.getAttribute('src');
|
|
557
|
+
const expectedSrc: string = images[index][1];
|
|
558
|
+
expect(src).toEqual(expectedSrc);
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
component.hostedUrl = `${RAW_LINK}${SUB_DIRECTORY}${CURRENT_MD_FILE}`;
|
|
564
|
+
fixture.detectChanges();
|
|
565
|
+
await fixture.whenStable();
|
|
566
|
+
checkImages();
|
|
567
|
+
|
|
568
|
+
component.hostedUrl = `${NON_RAW_LINK}${SUB_DIRECTORY}${CURRENT_MD_FILE}`;
|
|
569
|
+
fixture.detectChanges();
|
|
570
|
+
await fixture.whenStable();
|
|
571
|
+
checkImages();
|
|
572
|
+
|
|
573
|
+
component.hostedUrl = `${RELATIVE_LINK}${SUB_DIRECTORY}${CURRENT_MD_FILE}`;
|
|
574
|
+
fixture.detectChanges();
|
|
575
|
+
await fixture.whenStable();
|
|
576
|
+
checkImages();
|
|
577
|
+
}));
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
describe('Event bindings: ', () => {
|
|
581
|
+
describe('contentReady event: ', () => {
|
|
582
|
+
it('should be fired only once after display renders empty static content', waitForAsync(() => {
|
|
583
|
+
const fixture: ComponentFixture<TdMarkdownEmptyStaticContentTestEventsComponent> =
|
|
584
|
+
TestBed.createComponent(
|
|
585
|
+
TdMarkdownEmptyStaticContentTestEventsComponent
|
|
586
|
+
);
|
|
587
|
+
const component: TdMarkdownEmptyStaticContentTestEventsComponent =
|
|
588
|
+
fixture.debugElement.componentInstance;
|
|
589
|
+
|
|
590
|
+
jest.spyOn(component, 'tdMarkdownContentIsReady');
|
|
591
|
+
|
|
592
|
+
fixture.detectChanges();
|
|
593
|
+
fixture.whenStable().then(() => {
|
|
594
|
+
fixture.detectChanges();
|
|
595
|
+
expect(component.tdMarkdownContentIsReady).toHaveBeenCalledTimes(1);
|
|
596
|
+
});
|
|
597
|
+
}));
|
|
598
|
+
|
|
599
|
+
it('should be fired only once after display renders markup from static content', waitForAsync(() => {
|
|
600
|
+
const fixture: ComponentFixture<TdMarkdownStaticContentTestEventsComponent> =
|
|
601
|
+
TestBed.createComponent(TdMarkdownStaticContentTestEventsComponent);
|
|
602
|
+
const component: TdMarkdownStaticContentTestEventsComponent =
|
|
603
|
+
fixture.debugElement.componentInstance;
|
|
604
|
+
|
|
605
|
+
jest.spyOn(component, 'tdMarkdownContentIsReady');
|
|
606
|
+
|
|
607
|
+
fixture.detectChanges();
|
|
608
|
+
fixture.whenStable().then(() => {
|
|
609
|
+
fixture.detectChanges();
|
|
610
|
+
expect(component.tdMarkdownContentIsReady).toHaveBeenCalledTimes(1);
|
|
611
|
+
});
|
|
612
|
+
}));
|
|
613
|
+
|
|
614
|
+
it('should be fired only once after display renders initial markup from dynamic content', waitForAsync(() => {
|
|
615
|
+
const fixture: ComponentFixture<TdMarkdownDynamicContentTestEventsComponent> =
|
|
616
|
+
TestBed.createComponent(TdMarkdownDynamicContentTestEventsComponent);
|
|
617
|
+
const component: TdMarkdownDynamicContentTestEventsComponent =
|
|
618
|
+
fixture.debugElement.componentInstance;
|
|
619
|
+
jest.spyOn(component, 'tdMarkdownContentIsReady');
|
|
620
|
+
|
|
621
|
+
// Inital dynamic content
|
|
622
|
+
component.content = `
|
|
623
|
+
# another title
|
|
624
|
+
|
|
625
|
+
## subtitle
|
|
626
|
+
|
|
627
|
+
\`\`\`
|
|
628
|
+
pseudo code
|
|
629
|
+
\`\`\``;
|
|
630
|
+
|
|
631
|
+
fixture.detectChanges();
|
|
632
|
+
fixture.whenStable().then(() => {
|
|
633
|
+
fixture.detectChanges();
|
|
634
|
+
expect(component.tdMarkdownContentIsReady).toHaveBeenCalledTimes(1);
|
|
635
|
+
});
|
|
636
|
+
}));
|
|
637
|
+
|
|
638
|
+
it(`should be fired twice after changing the initial rendered markup dynamic content`, waitForAsync(() => {
|
|
639
|
+
const fixture: ComponentFixture<TdMarkdownDynamicContentTestEventsComponent> =
|
|
640
|
+
TestBed.createComponent(TdMarkdownDynamicContentTestEventsComponent);
|
|
641
|
+
const component: TdMarkdownDynamicContentTestEventsComponent =
|
|
642
|
+
fixture.debugElement.componentInstance;
|
|
643
|
+
jest.spyOn(component, 'tdMarkdownContentIsReady');
|
|
644
|
+
|
|
645
|
+
component.content = `
|
|
646
|
+
# another title
|
|
647
|
+
|
|
648
|
+
## subtitle
|
|
649
|
+
|
|
650
|
+
\`\`\`
|
|
651
|
+
pseudo code
|
|
652
|
+
\`\`\``;
|
|
653
|
+
|
|
654
|
+
fixture.detectChanges();
|
|
655
|
+
|
|
656
|
+
component.content = `
|
|
657
|
+
# changed title
|
|
658
|
+
|
|
659
|
+
## changed subtitle
|
|
660
|
+
|
|
661
|
+
\`\`\`
|
|
662
|
+
changed pseudo code
|
|
663
|
+
\`\`\``;
|
|
664
|
+
|
|
665
|
+
fixture.detectChanges();
|
|
666
|
+
|
|
667
|
+
fixture.whenStable().then(() => {
|
|
668
|
+
fixture.detectChanges();
|
|
669
|
+
expect(component.tdMarkdownContentIsReady).toHaveBeenCalledTimes(2);
|
|
670
|
+
});
|
|
671
|
+
}));
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Use the 3 components below to test the rendering requirements of the TdMarkdown component.
|
|
677
|
+
@Component({
|
|
678
|
+
template: ` <td-markdown></td-markdown> `,
|
|
679
|
+
imports: [TdMarkdownComponent],
|
|
680
|
+
})
|
|
681
|
+
class TdMarkdownEmptyStaticContentTestRenderingComponent {}
|
|
682
|
+
|
|
683
|
+
@Component({
|
|
684
|
+
// prettier-ignore
|
|
685
|
+
template: `
|
|
686
|
+
<td-markdown>
|
|
687
|
+
# title
|
|
688
|
+
|
|
689
|
+
* list item
|
|
690
|
+
</td-markdown>`,
|
|
691
|
+
preserveWhitespaces: true,
|
|
692
|
+
imports: [TdMarkdownComponent],
|
|
693
|
+
})
|
|
694
|
+
class TdMarkdownStaticContentTestRenderingComponent {}
|
|
695
|
+
|
|
696
|
+
@Component({
|
|
697
|
+
template: ` <td-markdown [content]="content"></td-markdown> `,
|
|
698
|
+
imports: [TdMarkdownComponent],
|
|
699
|
+
})
|
|
700
|
+
class TdMarkdownDymanicContentTestRenderingComponent {
|
|
701
|
+
content!: string;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
@Component({
|
|
705
|
+
// prettier-ignore
|
|
706
|
+
template: `
|
|
707
|
+
<td-markdown [simpleLineBreaks]="simpleLineBreaks">
|
|
708
|
+
first line
|
|
709
|
+
second line
|
|
710
|
+
third line
|
|
711
|
+
</td-markdown>
|
|
712
|
+
`,
|
|
713
|
+
preserveWhitespaces: true,
|
|
714
|
+
imports: [TdMarkdownComponent],
|
|
715
|
+
})
|
|
716
|
+
class TdMarkdownSimpleLineBreaksTestRenderingComponent {
|
|
717
|
+
simpleLineBreaks!: boolean;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Use the 3 components below to test event binding requirements of the TdMarkdown component.
|
|
721
|
+
@Component({
|
|
722
|
+
template: `
|
|
723
|
+
<td-markdown (contentReady)="tdMarkdownContentIsReady()"></td-markdown>
|
|
724
|
+
`,
|
|
725
|
+
imports: [TdMarkdownComponent],
|
|
726
|
+
})
|
|
727
|
+
class TdMarkdownEmptyStaticContentTestEventsComponent {
|
|
728
|
+
tdMarkdownContentIsReady(): void {
|
|
729
|
+
/* Stub */
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
@Component({
|
|
734
|
+
// prettier-ignore
|
|
735
|
+
template: `
|
|
736
|
+
<td-markdown (contentReady)="tdMarkdownContentIsReady()">
|
|
737
|
+
# title
|
|
738
|
+
|
|
739
|
+
* list item
|
|
740
|
+
</td-markdown>`,
|
|
741
|
+
preserveWhitespaces: true,
|
|
742
|
+
imports: [TdMarkdownComponent],
|
|
743
|
+
})
|
|
744
|
+
class TdMarkdownStaticContentTestEventsComponent {
|
|
745
|
+
tdMarkdownContentIsReady(): void {
|
|
746
|
+
/* Stub */
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
@Component({
|
|
751
|
+
template: `
|
|
752
|
+
<td-markdown
|
|
753
|
+
[content]="content"
|
|
754
|
+
(contentReady)="tdMarkdownContentIsReady()"
|
|
755
|
+
></td-markdown>
|
|
756
|
+
`,
|
|
757
|
+
imports: [TdMarkdownComponent],
|
|
758
|
+
})
|
|
759
|
+
class TdMarkdownDynamicContentTestEventsComponent {
|
|
760
|
+
content!: string;
|
|
761
|
+
tdMarkdownContentIsReady(): void {
|
|
762
|
+
/* Stub */
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
@Component({
|
|
767
|
+
template: `
|
|
768
|
+
<td-markdown [anchor]="anchor" [content]="content"></td-markdown>
|
|
769
|
+
`,
|
|
770
|
+
imports: [TdMarkdownComponent],
|
|
771
|
+
})
|
|
772
|
+
class TdMarkdownAnchorsTestEventsComponent {
|
|
773
|
+
content!: string;
|
|
774
|
+
anchor!: string;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
@Component({
|
|
778
|
+
template: `
|
|
779
|
+
<td-markdown [content]="content" [hostedUrl]="hostedUrl"></td-markdown>
|
|
780
|
+
`,
|
|
781
|
+
imports: [TdMarkdownComponent],
|
|
782
|
+
})
|
|
783
|
+
class TdMarkdownLinksTestEventsComponent {
|
|
784
|
+
hostedUrl!: string;
|
|
785
|
+
content!: string;
|
|
786
|
+
}
|