Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove new lines from the end of contextual text blocks #28

Merged
merged 2 commits into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions src/methods/aggregateDetails.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IBook, IBookAnnotation, ICombinedBooksAndHighlights } from '../types';
import { getBooks } from './getBooks';
import { getAnnotations } from './getAnnotations';
import { preserveNewlineIndentation, removeTrailingSpaces } from 'src/utils'

export const aggregateBookAndHighlightDetails = async (): Promise<ICombinedBooksAndHighlights[]> => {
const books = await getBooks();
Expand Down Expand Up @@ -30,7 +31,7 @@ export const aggregateBookAndHighlightDetails = async (): Promise<ICombinedBooks

return {
chapter: annotation.ZFUTUREPROOFING5,
contextualText: textForContext ? preserveNewlineIndentation(textForContext) : textForContext,
contextualText: textForContext ? removeTrailingSpaces(preserveNewlineIndentation(textForContext)) : textForContext,
highlight: preserveNewlineIndentation(annotation.ZANNOTATIONSELECTEDTEXT),
note: userNote ? preserveNewlineIndentation(userNote) : userNote,
highlightLocation: annotation.ZANNOTATIONLOCATION,
Expand All @@ -47,10 +48,3 @@ export const aggregateBookAndHighlightDetails = async (): Promise<ICombinedBooks

return resultingHighlights;
};

// Handler of double new line characters (\n\n) to preserve proper indentation in text blocks
const preserveNewlineIndentation = (textBlock: string): string => {
const stringWithNewLines = /\n+\s*/g;

return stringWithNewLines.test(textBlock) ? textBlock.replace(stringWithNewLines, '\n') : textBlock;
}
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export interface IHighlight {
highlight: string;
highlightLocation: string;
note: string;
highlightStyle: IBookAnnotation['ZANNOTATIONSTYLE'],
highlightCreationDate: number;
highlightModificationDate: number;
}
export interface ICombinedBooksAndHighlights {
bookTitle: string;
Expand Down
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './preserveNewlineIndentation'
export * from './removeTrailingSpaces'
6 changes: 6 additions & 0 deletions src/utils/preserveNewlineIndentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Handler of double new line characters (\n\n) to preserve proper indentation in text blocks
export const preserveNewlineIndentation = (textBlock: string): string => {
const stringWithNewLines = /\n+\s*/g;

return stringWithNewLines.test(textBlock) ? textBlock.replace(stringWithNewLines, '\n') : textBlock;
}
6 changes: 6 additions & 0 deletions src/utils/removeTrailingSpaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Handler of all space, tab or newline characters at the end of text blocks to prevent new lines appearing
export const removeTrailingSpaces = (textBlock: string): string => {
const endLineSpaces = /\s+$/;

return endLineSpaces.test(textBlock) ? textBlock.replace(endLineSpaces, '') : textBlock;
}
4 changes: 2 additions & 2 deletions test/mocks/aggregatedDetailsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const annotationsToAggregate = [{
"ZANNOTATIONASSETID": "THBFYNJKTGFTTVCGSAE5",
"ZFUTUREPROOFING5": "Another aggregated Introduction",
"ZANNOTATIONREPRESENTATIVETEXT": "This is a contextual text for the aggregated highlight from the Apple iPhone User Guide\n\ncontaining a new line to test the preservation of indentation",
"ZANNOTATIONSELECTEDTEXT": "aggregated highlight from the Apple iPhone User Guide\n\ncontaining a new line to test the preservation of indentation",
"ZANNOTATIONSELECTEDTEXT": "aggregated highlight from the Apple iPhone User Guide\n\ncontaining a new line to test the preservation of indentation\n\nand another new line\n\nto check one more time",
"ZANNOTATIONLOCATION": "aggregated-highlight-link-from-the-apple-iphone-user-guide",
"ZANNOTATIONNOTE": "Test note for the aggregated highlight from the Apple iPhone User Guide\n\nalong with a new line to test the preservation of indentation",
"ZANNOTATIONCREATIONDATE": 731876693.002279,
Expand Down Expand Up @@ -52,7 +52,7 @@ export const aggregatedHighlights = [{
}, {
"chapter": "Another aggregated Introduction",
"contextualText": "This is a contextual text for the aggregated highlight from the Apple iPhone User Guide\ncontaining a new line to test the preservation of indentation",
"highlight": "aggregated highlight from the Apple iPhone User Guide\ncontaining a new line to test the preservation of indentation",
"highlight": "aggregated highlight from the Apple iPhone User Guide\ncontaining a new line to test the preservation of indentation\nand another new line\nto check one more time",
"note": "Test note for the aggregated highlight from the Apple iPhone User Guide\nalong with a new line to test the preservation of indentation",
"highlightLocation": "aggregated-highlight-link-from-the-apple-iphone-user-guide",
"highlightStyle": 3,
Expand Down
17 changes: 17 additions & 0 deletions test/mocks/rawTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,20 @@ Number of annotations:: {{annotations.length}}

{{/each}}
`;

export const rawCustomTemplateMockWithWrappedTextBlockContainingNewlines = `Title:: 📕 {{{bookTitle}}}
Author:: {{{bookAuthor}}}
Link:: [Apple Books Link](ibooks://assetid/{{bookId}})

## Annotations

Number of annotations:: {{annotations.length}}

{{#each annotations}}
----

> [!QUOTE]
> {{{highlight}}}

{{/each}}
`;
29 changes: 28 additions & 1 deletion test/mocks/renderedTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Number of annotations:: 2
containing a new line to test the preservation of indentation
- 🎯 Highlight:: aggregated highlight from the Apple iPhone User Guide
containing a new line to test the preservation of indentation
and another new line
to check one more time
- 📝 Note:: Test note for the aggregated highlight from the Apple iPhone User Guide
along with a new line to test the preservation of indentation
- 📙 Highlight Link:: [Apple Books Highlight Link](ibooks://assetid/THBFYNJKTGFTTVCGSAE5#aggregated-highlight-link-from-the-apple-iphone-user-guide)
Expand Down Expand Up @@ -56,11 +58,36 @@ Number of annotations:: 2
- 🔖 Context:: This is a contextual text for the aggregated highlight from the Apple iPhone User Guide
containing a new line to test the preservation of indentation
- 🎯 Highlight:: <mark style="background:rgb(249,213,108); color:#000; padding:2px;">aggregated highlight from the Apple iPhone User Guide
containing a new line to test the preservation of indentation</mark>
containing a new line to test the preservation of indentation
and another new line
to check one more time</mark>
- 📝 Note:: Test note for the aggregated highlight from the Apple iPhone User Guide
along with a new line to test the preservation of indentation
- 📙 Highlight Link:: [Apple Books Highlight Link](ibooks://assetid/THBFYNJKTGFTTVCGSAE5#aggregated-highlight-link-from-the-apple-iphone-user-guide)
- <small>📅 Highlight taken on:: 2024-03-11 03:04:53 PM -04:00</small>
- <small>📅 Highlight modified on:: 2024-03-11 03:04:53 PM -04:00</small>

`;

export const renderedCustomTemplateMockWithWrappedTextBlockContainingNewlines = `Title:: 📕 Apple iPhone - User Guide - Instructions - with - restricted - symbols - in - title
Author:: Apple Inc.
Link:: [Apple Books Link](ibooks://assetid/THBFYNJKTGFTTVCGSAE5)

## Annotations

Number of annotations:: 2

----

> [!QUOTE]
> aggregated highlight from the Apple iPhone User Guide

----

> [!QUOTE]
> aggregated highlight from the Apple iPhone User Guide
containing a new line to test the preservation of indentation
and another new line
to check one more time

`;
156 changes: 156 additions & 0 deletions test/removeTrailingSpaces.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { describe, expect, test } from 'vitest'
import { removeTrailingSpaces } from 'src/utils'

describe('removeTrailingSpaces', () => {
test('Should remove a newline character at the end of text', () => {
const text = `This is an example text to test the removal of a newline character at the end of the text.\n`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of a newline character at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove double newline characters at the end of text', () => {
const text = `This is an example text to test the removal of double newline characters at the end of the text.\n\n`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of double newline characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove triple newline characters at the end of text', () => {
const text = `This is an example text to test the removal of triple newline characters at the end of the text.\n\n\n`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of triple newline characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove many newline characters at the end of text', () => {
const text = `This is an example text to test the removal of many newline characters at the end of the text.\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`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many newline characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove many newline characters at the end of text while preserving space, tab and newline characters within the text', () => {
const text = `This is an example text to test the removal of many newline characters at the end of the text while preserving space , tab\t and newline\n characters within the text.\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`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many newline characters at the end of the text while preserving space , tab\t and newline\n characters within the text.`

expect(actual).toEqual(expected)
})

test('Should remove a tab character at the end of text', () => {
const text = `This is an example text to test the removal of a tab character at the end of the text.\t`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of a tab character at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove double tab characters at the end of text', () => {
const text = `This is an example text to test the removal of double tab characters at the end of the text.\t\t`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of double tab characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove triple tab characters at the end of text', () => {
const text = `This is an example text to test the removal of triple tab characters at the end of the text.\t\t\t`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of triple tab characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove many tab characters at the end of text', () => {
const text = `This is an example text to test the removal of many tab characters at the end of the text.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many tab characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove many tab characters at the end of text while preserving space, tab and newline characters within the text', () => {
const text = `This is an example text to test the removal of many tab characters at the end of the text while preserving space , tab\t and newline\n characters within the text.\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many tab characters at the end of the text while preserving space , tab\t and newline\n characters within the text.`

expect(actual).toEqual(expected)
})

test('Should remove a space character at the end of text', () => {
const text = `This is an example text to test the removal of a space character at the end of the text. `
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of a space character at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove double space characters at the end of text', () => {
const text = `This is an example text to test the removal of double space characters at the end of the text. `
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of double space characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove triple space characters at the end of text', () => {
const text = `This is an example text to test the removal of triple space characters at the end of the text. `
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of triple space characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove many space characters at the end of text', () => {
const text = `This is an example text to test the removal of many space characters at the end of the text. `
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many space characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove many space characters at the end of text while preserving space, tab and newline characters at the end of the text.', () => {
const text = `This is an example text to test the removal of many space characters at the end of the text preserving space , tab\b and newline\n characters at the end of the text. `
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many space characters at the end of the text preserving space , tab\b and newline\n characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove multiple space, tab and newline characters at the end of text', () => {
const text = `This is an example text to test the removal of many space, tab and newline characters at the end of the text. \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t `
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many space, tab and newline characters at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should remove multiple space, tab and newline characters at the end of text while preserving space, tab and newline characters within text', () => {
const text = `This is an example text to test the removal of many space , tab\t and newline\n characters at the end of the text while preserving space, tab and newline characters within text. \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t `
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test the removal of many space , tab\t and newline\n characters at the end of the text while preserving space, tab and newline characters within text.`

expect(actual).toEqual(expected)
})

test('Should return the text when no space, tab or newline characters exist at the end of the text', () => {
const text = `This is an example text to test that the text is returned when no space, tab or newline characters exist at the end of the text.`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test that the text is returned when no space, tab or newline characters exist at the end of the text.`

expect(actual).toEqual(expected)
})

test('Should return the text when no space, tab or newline characters exist at the end of the text while preserving space, tab and newline characters within text', () => {
const text = `This is an example text to test that the text is returned when no space , tab\t or newline\n characters exist at the end of the text while preserving space, tab and newline characters within text.`
const actual = removeTrailingSpaces(text)
const expected = `This is an example text to test that the text is returned when no space , tab\t or newline\n characters exist at the end of the text while preserving space, tab and newline characters within text.`

expect(actual).toEqual(expected)
})
})
15 changes: 11 additions & 4 deletions test/renderHighlightsTemplate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import Handlebars from 'handlebars';
import { describe, expect, test, vi } from 'vitest';
import { renderHighlightsTemplate } from '../src/methods/renderHighlightsTemplate';
import { aggregatedHighlights } from './mocks/aggregatedDetailsData';
import { rawCustomTemplateMock } from './mocks/rawTemplates';
import { defaultTemplateMock, renderedCustomTemplateMock } from './mocks/renderedTemplate';
import { rawCustomTemplateMock, rawCustomTemplateMockWithWrappedTextBlockContainingNewlines } from './mocks/rawTemplates';
import { defaultTemplateMock, renderedCustomTemplateMock, renderedCustomTemplateMockWithWrappedTextBlockContainingNewlines } from './mocks/renderedTemplate';
import defaultTemplate from '../src/template';
import { ICombinedBooksAndHighlights } from 'src/types';

describe('renderHighlightsTemplate', () => {
const helpers = Handlebars.helpers;
Expand All @@ -18,18 +19,24 @@ describe('renderHighlightsTemplate', () => {

describe('Template rendering', () => {
test('Should render a default template with the provided data', async () => {
const renderedTemplate = await renderHighlightsTemplate(aggregatedHighlights[0], defaultTemplate);
const renderedTemplate = await renderHighlightsTemplate(aggregatedHighlights[0] as ICombinedBooksAndHighlights, defaultTemplate);

expect(renderedTemplate).toEqual(defaultTemplateMock);
});

test('Should render a custom template with the provided data', async () => {
tzSpy.mockImplementation(() => 'America/New_York');

const renderedTemplate = await renderHighlightsTemplate(aggregatedHighlights[0], rawCustomTemplateMock);
const renderedTemplate = await renderHighlightsTemplate(aggregatedHighlights[0] as ICombinedBooksAndHighlights, rawCustomTemplateMock);

expect(renderedTemplate).toEqual(renderedCustomTemplateMock);
});

test('Should render a custom template with the provided data and preserve newlines in wrapped text blocks', async () => {
const renderedTemplate = await renderHighlightsTemplate(aggregatedHighlights[0] as ICombinedBooksAndHighlights, rawCustomTemplateMockWithWrappedTextBlockContainingNewlines);

expect(renderedTemplate).toEqual(renderedCustomTemplateMockWithWrappedTextBlockContainingNewlines);
});
});

describe('Custom Handlebars helpers', () => {
Expand Down
Loading