@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.
@@ -0,0 +1,16 @@
1
+ # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2
+ # For additional information regarding the format and rule options, please see:
3
+ # https://github.com/browserslist/browserslist#queries
4
+
5
+ # For the full list of supported browsers by the Angular framework, please see:
6
+ # https://angular.dev/reference/versions#browser-support
7
+
8
+ # You can see what browsers were selected by your queries by running:
9
+ # npx browserslist
10
+
11
+ last 1 Chrome version
12
+ last 1 Firefox version
13
+ last 2 Edge major versions
14
+ last 2 Safari major versions
15
+ last 2 iOS major versions
16
+ Firefox ESR
package/.eslintrc.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "extends": ["../../.eslintrc.json"],
3
+ "ignorePatterns": ["!**/*"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts"],
7
+ "extends": [
8
+ "plugin:@nx/angular",
9
+ "plugin:@angular-eslint/template/process-inline-templates"
10
+ ],
11
+ "rules": {
12
+ "@angular-eslint/directive-selector": [
13
+ "error",
14
+ {
15
+ "type": "attribute",
16
+ "prefix": "td",
17
+ "style": "camelCase"
18
+ }
19
+ ],
20
+ "@angular-eslint/component-selector": [
21
+ "error",
22
+ {
23
+ "type": "element",
24
+ "prefix": "td",
25
+ "style": "kebab-case"
26
+ }
27
+ ]
28
+ }
29
+ },
30
+ {
31
+ "files": ["*.html"],
32
+ "extends": ["plugin:@nx/angular-template"],
33
+ "rules": {}
34
+ }
35
+ ]
36
+ }
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ ## TdMarkdownComponent: td-markdown
2
+
3
+ `<td-markdown>` is a component that parses and renders Github flavored markdown. It is based on the [showdown](https://github.com/showdownjs/showdown/) library.
4
+
5
+ **Note:** This module uses the **DomSanitizer** service to sanitize the parsed html from the showdown lib to avoid **XSS** issues.
6
+
7
+ By default, `--dev` build will log the following message in the console to let you know:
8
+
9
+ `WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).`
10
+
11
+ ## API Summary
12
+
13
+ #### Inputs
14
+
15
+ - content?: string
16
+
17
+ - Markdown format content to be parsed as html markup.
18
+ - Used to load data dynamically. e.g. `README.md` content.
19
+
20
+ - simpleLineBreaks?: string
21
+
22
+ - Sets whether newline characters inside paragraphs and spans are parsed as <br/>.
23
+ - Defaults to false.
24
+
25
+ - hostedUrl?: string
26
+
27
+ - If markdown contains relative paths, this is required to generate correct urls.
28
+
29
+ - anchor?: string
30
+
31
+ - Anchor to jump to.
32
+
33
+ - fileLinkExtensions?: string[]
34
+ - The file extensions to monitor within anchor tags.
35
+ - Clicking links that end with these extensions will prevent the default action and emit 'fileClicked' event.
36
+
37
+ #### Events
38
+
39
+ - contentReady: undefined
40
+
41
+ - Event emitted after the markdown content rendering is finished.
42
+
43
+ - fileLinkClicked: URL
44
+ - Emitted when an anchor tag is clicked and its 'href' matches one of the extensions in 'fileLinkExtensions'.
45
+
46
+ ## Installation
47
+
48
+ This component can be installed as an npm package.
49
+
50
+ ```bash
51
+ npm i -save @covalent/markdown
52
+ ```
53
+
54
+ ## Setup
55
+
56
+ Then, import the **CovalentMarkdownModule** in your NgModule:
57
+
58
+ ```typescript
59
+ import { CovalentMarkdownModule } from '@covalent/markdown';
60
+ @NgModule({
61
+ imports: [
62
+ CovalentMarkdownModule,
63
+ ...
64
+ ],
65
+ ...
66
+ })
67
+ export class MyModule {}
68
+ ```
69
+
70
+ ### Theming
71
+
72
+ This module comes with its own Covalent theme which uses the material theme which is used by importing our theme scss file.
73
+
74
+ ```scss
75
+ @use '@angular/material/theming' as mat;
76
+ @use '@covalent/markdown/markdown-theme' as markdown;
77
+
78
+ @include mat.core();
79
+
80
+ $primary: mat.define-palette($mat-orange, 800);
81
+ $accent: mat.define-palette($mat-light-blue, 600, A100, A400);
82
+ $warn: mat.define-palette($mat-red, 600);
83
+
84
+ $theme: mat.define-light-theme($primary, $accent, $warn);
85
+
86
+ @include markdown.covalent-markdown-theme($theme);
87
+ ```
88
+
89
+ ## Example
90
+
91
+ **Html:**
92
+
93
+ ```html
94
+ <td-markdown> # Heading ## Sub Heading (H2) ### Steps (H2) </td-markdown>
95
+ ```
96
+
97
+ **Output:**
98
+
99
+ # Heading
100
+
101
+ ## Sub Heading (H2)
102
+
103
+ ### Steps (H2)
@@ -0,0 +1,72 @@
1
+ @use '@angular/material' as mat;
2
+
3
+ @mixin covalent-markdown-theme($theme) {
4
+ $accent: map-get($theme, accent);
5
+ $foreground: map-get($theme, foreground);
6
+ $background: map-get($theme, background);
7
+
8
+ td-markdown {
9
+ a {
10
+ color: mat.m2-get-color-from-palette($accent);
11
+ }
12
+
13
+ h1,
14
+ h2 {
15
+ border-bottom-color: mat.m2-get-color-from-palette($foreground, divider);
16
+ }
17
+
18
+ h3,
19
+ h4,
20
+ h5,
21
+ h6 {
22
+ color: mat.m2-get-color-from-palette($foreground, secondary-text);
23
+ }
24
+
25
+ hr {
26
+ border-color: mat.m2-get-color-from-palette($foreground, divider);
27
+ }
28
+
29
+ blockquote {
30
+ color: mat.m2-get-color-from-palette($foreground, secondary-text);
31
+ border-left-color: mat.m2-get-color-from-palette($foreground, divider);
32
+ }
33
+
34
+ table {
35
+ th,
36
+ td {
37
+ border-color: mat.m2-get-color-from-palette($foreground, dividers);
38
+ }
39
+
40
+ tr {
41
+ border-top-color: mat.m2-get-color-from-palette($foreground, dividers);
42
+
43
+ &:nth-child(2n) {
44
+ background-color: mat.m2-get-color-from-palette(
45
+ $foreground,
46
+ dividers
47
+ );
48
+ }
49
+ }
50
+ }
51
+
52
+ img {
53
+ background-color: mat.m2-get-color-from-palette($background, card);
54
+ }
55
+
56
+ code {
57
+ background-color: mat.m2-get-color-from-palette($background, hover);
58
+ }
59
+
60
+ .highlight pre,
61
+ pre {
62
+ background-color: mat.m2-get-color-from-palette($background, app-bar);
63
+ }
64
+
65
+ kbd {
66
+ color: mat.m2-get-color-from-palette($foreground, secondary-text);
67
+ background-color: mat.m2-get-color-from-palette($background, app-bar);
68
+ border-color: mat.m2-get-color-from-palette($foreground, divider);
69
+ border-bottom-color: mat.m2-get-color-from-palette($foreground, disabled);
70
+ }
71
+ }
72
+ }
package/jest.config.js ADDED
@@ -0,0 +1,23 @@
1
+ module.exports = {
2
+ displayName: 'markdown',
3
+ preset: '../../jest.preset.js',
4
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
5
+ globals: {},
6
+ coverageDirectory: '../../coverage/libs/markdown',
7
+ testEnvironment: 'jsdom',
8
+ transform: {
9
+ '^.+\\.(ts|mjs|js|html)$': [
10
+ 'jest-preset-angular',
11
+ {
12
+ tsconfig: '<rootDir>/tsconfig.spec.json',
13
+ stringifyContentPathRegex: '\\.(html|svg)$',
14
+ },
15
+ ],
16
+ },
17
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
18
+ snapshotSerializers: [
19
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
20
+ 'jest-preset-angular/build/serializers/ng-snapshot',
21
+ 'jest-preset-angular/build/serializers/html-comment',
22
+ ],
23
+ };
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/libs/markdown",
4
+ "assets": ["./**/*theme.scss"]
5
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@covalent/markdown",
3
+ "version": "0.0.0-COVALENT",
4
+ "description": "Teradata UI Platform Markdown Module",
5
+ "keywords": [
6
+ "angular",
7
+ "components",
8
+ "reusable",
9
+ "markdown"
10
+ ],
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/teradata/covalent.git"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/teradata/covalent/issues"
17
+ },
18
+ "license": "MIT",
19
+ "author": "Teradata UX",
20
+ "peerDependencies": {
21
+ "showdown": "0.0.0-SHOWDOWN",
22
+ "@angular/common": "0.0.0-NG",
23
+ "@angular/core": "0.0.0-NG",
24
+ "@angular/platform-browser": "0.0.0-NG"
25
+ },
26
+ "dependencies": {
27
+ "tslib": "0.0.0-TSLIB"
28
+ },
29
+ "exports": {
30
+ "./markdown-theme": {
31
+ "sass": "./_markdown-theme.scss"
32
+ }
33
+ }
34
+ }
package/project.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "markdown",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "projectType": "library",
5
+ "sourceRoot": "libs/markdown/src",
6
+ "prefix": "covalent",
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@angular-devkit/build-angular:ng-packagr",
10
+ "outputs": ["{workspaceRoot}/dist/libs/markdown"],
11
+ "options": {
12
+ "project": "libs/markdown/ng-package.json"
13
+ },
14
+ "configurations": {
15
+ "production": {
16
+ "tsConfig": "libs/markdown/tsconfig.lib.prod.json"
17
+ },
18
+ "development": {
19
+ "tsConfig": "libs/markdown/tsconfig.lib.json"
20
+ }
21
+ },
22
+ "defaultConfiguration": "production",
23
+ "dependsOn": [
24
+ {
25
+ "projects": ["angular", "angular-highlight"],
26
+ "target": "build",
27
+ "params": "ignore"
28
+ }
29
+ ]
30
+ },
31
+ "test": {
32
+ "executor": "@nrwl/jest:jest",
33
+ "outputs": ["{workspaceRoot}/coverage/libs/markdown"],
34
+ "options": {
35
+ "jestConfig": "libs/markdown/jest.config.js",
36
+ "passWithNoTests": true
37
+ }
38
+ },
39
+ "lint": {
40
+ "executor": "@nx/eslint:lint",
41
+ "options": {
42
+ "lintFilePatterns": [
43
+ "libs/markdown/src/**/*.ts",
44
+ "libs/markdown/src/**/*.html",
45
+ "libs/markdown/flavored/**/*.ts",
46
+ "libs/markdown/flavored/**/*.html",
47
+ "libs/markdown/navigator/**/*.ts",
48
+ "libs/markdown/navigator/**/*.html"
49
+ ]
50
+ }
51
+ },
52
+ "scsslint": {
53
+ "executor": "nx:run-commands",
54
+ "options": {
55
+ "commands": [
56
+ {
57
+ "command": "./node_modules/.bin/stylelint --allow-empty-input 'libs/markdown/**/*.scss'"
58
+ }
59
+ ]
60
+ }
61
+ }
62
+ },
63
+ "tags": []
64
+ }
@@ -0,0 +1,101 @@
1
+ import { TestBed, inject, waitForAsync } from '@angular/core/testing';
2
+ import { TdMarkdownLoaderService } from './markdown-loader.service';
3
+ import { CovalentMarkdownModule } from '../markdown.module';
4
+
5
+ const SAMPLE_HEADING = 'Covalent Design System';
6
+
7
+ const RAW_GH_URL =
8
+ 'https://raw.githubusercontent.com/Teradata/covalent/main/README.md';
9
+ const GH_URL = 'https://github.com/Teradata/covalent/blob/main/README.md';
10
+ const BRANCH_GH_URL =
11
+ 'https://github.com/Teradata/covalent/blob/main/README.md';
12
+ const RAW_GH_BRANCH_URL =
13
+ 'https://raw.githubusercontent.com/Teradata/covalent/main/README.md';
14
+ const NON_MARKDOWN_URL = 'https://teradata.github.io/covalent/#/';
15
+ const UNREACHABLE_URL = 'https://github.com/Teradata/covalent/tree/main/src';
16
+
17
+ describe('Service: MarkdownLoader', () => {
18
+ beforeEach(waitForAsync(() => {
19
+ TestBed.configureTestingModule({
20
+ imports: [CovalentMarkdownModule],
21
+ });
22
+ }));
23
+
24
+ it('should fetch from a raw github url', waitForAsync(
25
+ inject(
26
+ [TdMarkdownLoaderService],
27
+ async (_markdownLoaderService: TdMarkdownLoaderService) => {
28
+ const markdown: string = await _markdownLoaderService.load(RAW_GH_URL);
29
+ expect(markdown).toContain(SAMPLE_HEADING);
30
+ }
31
+ )
32
+ ));
33
+
34
+ it('should fetch from a non-raw github url', waitForAsync(
35
+ inject(
36
+ [TdMarkdownLoaderService],
37
+ async (_markdownLoaderService: TdMarkdownLoaderService) => {
38
+ const markdown: string = await _markdownLoaderService.load(GH_URL);
39
+ expect(markdown).toContain(SAMPLE_HEADING);
40
+ }
41
+ )
42
+ ));
43
+
44
+ it('should fetch from a raw branch github url', waitForAsync(
45
+ inject(
46
+ [TdMarkdownLoaderService],
47
+ async (_markdownLoaderService: TdMarkdownLoaderService) => {
48
+ const markdown: string = await _markdownLoaderService.load(
49
+ RAW_GH_BRANCH_URL
50
+ );
51
+ expect(markdown).toContain(SAMPLE_HEADING);
52
+ }
53
+ )
54
+ ));
55
+
56
+ it('should fetch from a non-raw branch github url', waitForAsync(
57
+ inject(
58
+ [TdMarkdownLoaderService],
59
+ async (_markdownLoaderService: TdMarkdownLoaderService) => {
60
+ const markdown: string = await _markdownLoaderService.load(
61
+ BRANCH_GH_URL
62
+ );
63
+ expect(markdown).toContain(SAMPLE_HEADING);
64
+ }
65
+ )
66
+ ));
67
+
68
+ it('should throw error if content response type is not plain or markdown', waitForAsync(
69
+ inject(
70
+ [TdMarkdownLoaderService],
71
+ async (_markdownLoaderService: TdMarkdownLoaderService) => {
72
+ let failed = false;
73
+ try {
74
+ await _markdownLoaderService.load(NON_MARKDOWN_URL);
75
+ } catch (error) {
76
+ failed = true;
77
+ } finally {
78
+ expect(failed).toBeTruthy();
79
+ }
80
+ }
81
+ )
82
+ ));
83
+
84
+ it('should throw error if url is not reachable', waitForAsync(
85
+ inject(
86
+ [TdMarkdownLoaderService],
87
+ async (_markdownLoaderService: TdMarkdownLoaderService) => {
88
+ let failed = false;
89
+ try {
90
+ await _markdownLoaderService.load(UNREACHABLE_URL);
91
+ } catch (error) {
92
+ failed = true;
93
+ } finally {
94
+ expect(failed).toBeTruthy();
95
+ }
96
+ }
97
+ )
98
+ ));
99
+
100
+ // TODO: test http params
101
+ });
@@ -0,0 +1,35 @@
1
+ import { Injectable, SecurityContext } from '@angular/core';
2
+ import { DomSanitizer } from '@angular/platform-browser';
3
+ import { HttpClient, HttpResponse } from '@angular/common/http';
4
+ import { isGithubHref, rawGithubHref } from '../markdown-utils/markdown-utils';
5
+
6
+ @Injectable()
7
+ export class TdMarkdownLoaderService {
8
+ constructor(private _http: HttpClient, private _sanitizer: DomSanitizer) {}
9
+
10
+ async load(url: string, httpOptions: object = {}): Promise<string> {
11
+ const sanitizedUrl: string =
12
+ this._sanitizer.sanitize(SecurityContext.URL, url) ?? '';
13
+ let urlToGet: string = sanitizedUrl;
14
+ if (isGithubHref(sanitizedUrl)) {
15
+ urlToGet = rawGithubHref(sanitizedUrl);
16
+ }
17
+
18
+ const response: HttpResponse<string> | undefined = await this._http
19
+ .get(urlToGet, {
20
+ ...httpOptions,
21
+ responseType: 'text',
22
+ observe: 'response',
23
+ })
24
+ .toPromise();
25
+ const contentType: string = response?.headers.get('Content-Type') ?? '';
26
+ if (
27
+ contentType.includes('text/plain') ||
28
+ contentType.includes('text/markdown')
29
+ ) {
30
+ return response?.body ?? '';
31
+ } else {
32
+ throw Error(`${contentType} is not a handled content type`);
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,189 @@
1
+ import {
2
+ removeLeadingHash,
3
+ removeTrailingHash,
4
+ genHeadingId,
5
+ isAnchorLink,
6
+ isGithubHref,
7
+ rawGithubHref,
8
+ scrollToAnchor,
9
+ isRawGithubHref,
10
+ } from './markdown-utils';
11
+ import { Component } from '@angular/core';
12
+ import { TestBed, ComponentFixture, waitForAsync } from '@angular/core/testing';
13
+ import { By } from '@angular/platform-browser';
14
+
15
+ // Implementing scrollIntoView since its not implemented JSDOM
16
+ window.HTMLElement.prototype.scrollIntoView = function () {
17
+ // noop
18
+ };
19
+
20
+ @Component({
21
+ selector: 'td-jump-to-anchor-test-component',
22
+ template: `
23
+ <main>
24
+ <section id="s1">
25
+ <article id="a1">
26
+ <h1 id="heading1">heading 1</h1>
27
+ <h1 id="heading2">heading 2</h1>
28
+ </article>
29
+ <article id="a2">
30
+ <h1 id="heading3">heading 3</h1>
31
+ <h1 id="heading4">heading 4</h1>
32
+ </article>
33
+ </section>
34
+
35
+ <section id="s2">
36
+ <article id="a3">
37
+ <h1 id="heading5">heading 5</h1>
38
+ <h1 id="heading6">heading 6</h1>
39
+ </article>
40
+ <article id="a4">
41
+ <h1 id="heading7">heading 7</h1>
42
+ <h1 id="heading8">heading 8</h1>
43
+ </article>
44
+ </section>
45
+ </main>
46
+ `,
47
+ })
48
+ class JumpToAnchorTestComponent {}
49
+
50
+ describe('Markdown utils', () => {
51
+ beforeEach(
52
+ waitForAsync(() => {
53
+ TestBed.configureTestingModule({
54
+ imports: [JumpToAnchorTestComponent],
55
+ }).compileComponents();
56
+ })
57
+ );
58
+
59
+ it('removeLeadingHash should remove leading hashes', () => {
60
+ expect(removeLeadingHash('')).toBe('');
61
+ expect(removeLeadingHash('#')).toBe('');
62
+ expect(removeLeadingHash('#anchor')).toBe('anchor');
63
+ expect(removeLeadingHash('##anchor')).toBe('anchor');
64
+ expect(removeLeadingHash('#anchor#')).toBe('anchor#');
65
+ expect(removeLeadingHash('#before#anchor')).toBe('before#anchor');
66
+ });
67
+
68
+ it('removeTrailingHash should remove trailing hashes', () => {
69
+ expect(removeTrailingHash('')).toBe('');
70
+ expect(removeTrailingHash('#')).toBe('');
71
+ expect(removeTrailingHash('#anchor')).toBe('');
72
+ expect(removeTrailingHash('before#')).toBe('before');
73
+ expect(removeTrailingHash('even-before#before#')).toBe('even-before');
74
+ expect(removeTrailingHash('before##')).toBe('before');
75
+ });
76
+
77
+ it('genHeadingId should generate proper heading id', () => {
78
+ expect(genHeadingId('')).toBe('');
79
+ expect(genHeadingId('Markdown Utils')).toBe('markdownutils');
80
+ expect(genHeadingId('Markdown-Utils')).toBe('markdownutils');
81
+ expect(genHeadingId('Markdown_Utils')).toBe('markdownutils');
82
+ expect(genHeadingId('(component/module/utility)')).toBe(
83
+ 'componentmoduleutility'
84
+ );
85
+ expect(genHeadingId('Markdown Utils!?')).toBe('markdownutils');
86
+ expect(genHeadingId('#markdown_utils')).toBe('markdownutils');
87
+ expect(genHeadingId('#markdown-utils')).toBe('markdownutils');
88
+ expect(genHeadingId('#markdown utils')).toBe('markdownutils');
89
+ expect(genHeadingId('#(component/module/utility)')).toBe(
90
+ 'componentmoduleutility'
91
+ );
92
+ expect(genHeadingId('#')).toBe('');
93
+ });
94
+
95
+ it('isAnchorLink should generate proper heading id', () => {
96
+ expect(isAnchorLink(undefined)).toBe(false);
97
+ const anchor: HTMLAnchorElement = document.createElement('a');
98
+ expect(isAnchorLink(anchor)).toBe(false);
99
+ anchor.setAttribute('href', '');
100
+ expect(isAnchorLink(anchor)).toBe(false);
101
+ anchor.setAttribute('href', 'github.com');
102
+ expect(isAnchorLink(anchor)).toBe(false);
103
+ anchor.setAttribute('href', '#anchor');
104
+ expect(isAnchorLink(anchor)).toBe(true);
105
+ });
106
+
107
+ it('rawGithubHref should generate raw github href', () => {
108
+ expect(rawGithubHref('')).toBe('');
109
+ expect(rawGithubHref(undefined)).toBe('');
110
+ expect(rawGithubHref('invalid-href')).toBe('');
111
+ expect(rawGithubHref('http://non-github-url.com')).toBe('');
112
+
113
+ const something = `https://github.com/Teradata/covalent/blob/`;
114
+ const somethingElse =
115
+ 'https://raw.githubusercontent.com/Teradata/covalent/';
116
+ const path = '/docs/CONTRIBUTING.md';
117
+ const developBranch = 'develop';
118
+ const mainBranch = 'main';
119
+ const anchor = '#anchor';
120
+
121
+ const DEVELOP_NON_RAW_HREF = `${something}${developBranch}${path}`;
122
+ const DEVELOP_RAW_HREF = `${somethingElse}${developBranch}${path}`;
123
+ const MAIN_NON_RAW_HREF = `${something}${mainBranch}${path}`;
124
+ const MAIN_RAW_HREF = `${somethingElse}${mainBranch}${path}`;
125
+ const DEVELOP_NON_RAW_HREF_WITH_ANCHOR = `${something}${developBranch}${path}${anchor}`;
126
+ const DEVELOP_RAW_HREF_WITH_ANCHOR = `${somethingElse}${developBranch}${path}${anchor}`;
127
+
128
+ expect(rawGithubHref(anchor)).toBe('');
129
+ expect(rawGithubHref(DEVELOP_NON_RAW_HREF)).toBe(DEVELOP_RAW_HREF);
130
+ expect(rawGithubHref(DEVELOP_RAW_HREF)).toBe(DEVELOP_RAW_HREF);
131
+ expect(rawGithubHref(MAIN_NON_RAW_HREF)).toBe(MAIN_RAW_HREF);
132
+ expect(rawGithubHref(MAIN_RAW_HREF)).toBe(MAIN_RAW_HREF);
133
+ expect(rawGithubHref(DEVELOP_NON_RAW_HREF_WITH_ANCHOR)).toBe(
134
+ DEVELOP_RAW_HREF_WITH_ANCHOR
135
+ );
136
+ expect(rawGithubHref(DEVELOP_RAW_HREF_WITH_ANCHOR)).toBe(
137
+ DEVELOP_RAW_HREF_WITH_ANCHOR
138
+ );
139
+ });
140
+
141
+ it('isGithubHref should check whether an href is from github.com', () => {
142
+ expect(isGithubHref('')).toBe(false);
143
+ expect(isGithubHref(undefined)).toBe(false);
144
+ expect(isGithubHref('github.com')).toBe(false);
145
+ expect(isGithubHref('subdomain.github.com')).toBe(false);
146
+ expect(isGithubHref('https://raw.githubusercontent.com')).toBe(false);
147
+ expect(isGithubHref('http://github.com')).toBe(true);
148
+ expect(isGithubHref('https://github.com')).toBe(true);
149
+ expect(isGithubHref('https://github.com/something')).toBe(true);
150
+ });
151
+
152
+ it('isRawGithubHref should check whether an href is from raw.githubusercontent.com', () => {
153
+ expect(isRawGithubHref('')).toBe(false);
154
+ expect(isRawGithubHref(undefined)).toBe(false);
155
+ expect(isRawGithubHref('raw.githubusercontent.com')).toBe(false);
156
+ expect(isRawGithubHref('https://raw.githubusercontent.com')).toBe(true);
157
+ expect(isRawGithubHref('http://raw.githubusercontent.com')).toBe(true);
158
+ expect(isRawGithubHref('https://raw.githubusercontent.com')).toBe(true);
159
+ expect(isRawGithubHref('https://raw.githubusercontent.com/something')).toBe(
160
+ true
161
+ );
162
+ });
163
+
164
+ it('scrollToAnchor should scroll to anchor within provided element, or parent if tryParent is true', async () => {
165
+ const fixture: ComponentFixture<JumpToAnchorTestComponent> =
166
+ TestBed.createComponent(JumpToAnchorTestComponent);
167
+
168
+ fixture.detectChanges();
169
+ await fixture.whenRenderingDone();
170
+ await fixture.whenStable();
171
+
172
+ const a1: HTMLDivElement = fixture.debugElement.query(
173
+ By.css('#a1')
174
+ ).nativeElement;
175
+
176
+ expect(scrollToAnchor(a1, 'heading 1', false)).toBeTruthy();
177
+ expect(scrollToAnchor(a1, 'heading 1', true)).toBeTruthy();
178
+ expect(scrollToAnchor(a1, 'heading 3', false)).toBeFalsy();
179
+ expect(scrollToAnchor(a1, 'heading 3', true)).toBeTruthy();
180
+
181
+ expect(scrollToAnchor(a1, 'heading 5', false)).toBeFalsy();
182
+ expect(scrollToAnchor(a1, 'heading 5', true)).toBeFalsy();
183
+
184
+ expect(scrollToAnchor(a1, 'heading1', false)).toBeTruthy();
185
+ expect(scrollToAnchor(a1, 'heading-1', false)).toBeTruthy();
186
+ expect(scrollToAnchor(a1, 'heading_1', false)).toBeTruthy();
187
+ expect(scrollToAnchor(a1, 'heading 1!', false)).toBeTruthy();
188
+ });
189
+ });