diff --git a/src/__tests__/lexer/account_directives.test.ts b/src/__tests__/lexer/account_directives.test.ts index d9b2cda..fd1cfac 100644 --- a/src/__tests__/lexer/account_directives.test.ts +++ b/src/__tests__/lexer/account_directives.test.ts @@ -1,86 +1,77 @@ import test from 'ava'; -import { runLexerTests } from './utils'; +import { macro } from './utils'; -const tests = [ - { - pattern: 'account Assets:Chequing ', - expected: [ - 'AccountDirective', - { AccountName: ['Assets', 'Chequing'] }, - 'DOUBLE_WS' - ], - title: 'recognize account directive and name with double-space at end' - }, - { - pattern: 'account Assets:Chequing\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets', 'Chequing'] }, - 'NEWLINE' - ], - title: 'recognize account directive and name with end of line at end' - }, - { - pattern: 'account Assets:Chequing ; a comment\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets', 'Chequing'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE' - ], - title: 'recognize account directive and name with comment at end' - }, - { - pattern: 'account Assets:Chequing ; a comment with: a tag\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets', 'Chequing'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'NEWLINE' - ], - title: 'recognize account directive and name with comment with a tag at end' - }, - { - pattern: 'account Assets:Chequing\n ; a comment\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets', 'Chequing'] }, - 'NEWLINE', - 'INDENT', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE' - ], - title: 'recognize account directive and name with comment on the next line' - }, - { - pattern: 'account (Assets:Chequing) ', - expected: [ - 'AccountDirective', - { AccountName: ['(Assets', 'Chequing)'] }, - 'DOUBLE_WS' - ], - title: - 'ignore ( and treat it as part of the account name of a real account (hledger bug)' - }, - { - pattern: 'account [Assets:Chequing] ', - expected: [ - 'AccountDirective', - { AccountName: ['[Assets', 'Chequing]'] }, - 'DOUBLE_WS' - ], - title: - 'ignore [ and treat it as part of the account name of a real account (hledger bug)' - } -]; +test( + 'recognize account directive and name with double-space at end', + macro, + 'account Assets:Chequing ', + ['AccountDirective', { AccountName: ['Assets', 'Chequing'] }, 'DOUBLE_WS'] +); -runLexerTests(test, tests); +test( + 'recognize account directive and name with end of line at end', + macro, + 'account Assets:Chequing\n', + ['AccountDirective', { AccountName: ['Assets', 'Chequing'] }, 'NEWLINE'] +); + +test( + 'recognize account directive and name with comment at end', + macro, + 'account Assets:Chequing ; a comment\n', + [ + 'AccountDirective', + { AccountName: ['Assets', 'Chequing'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test( + 'recognize account directive and name with comment with a tag at end', + macro, + 'account Assets:Chequing ; a comment with: a tag\n', + [ + 'AccountDirective', + { AccountName: ['Assets', 'Chequing'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'NEWLINE' + ] +); + +test( + 'recognize account directive and name with comment on the next line', + macro, + 'account Assets:Chequing\n ; a comment\n', + [ + 'AccountDirective', + { AccountName: ['Assets', 'Chequing'] }, + 'NEWLINE', + 'INDENT', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test( + 'ignore ( and treat it as part of the account name of a real account (hledger bug)', + macro, + 'account (Assets:Chequing) ', + ['AccountDirective', { AccountName: ['(Assets', 'Chequing)'] }, 'DOUBLE_WS'] +); + +test( + 'ignore [ and treat it as part of the account name of a real account (hledger bug)', + macro, + 'account [Assets:Chequing] ', + ['AccountDirective', { AccountName: ['[Assets', 'Chequing]'] }, 'DOUBLE_WS'] +); diff --git a/src/__tests__/lexer/amounts.test.ts b/src/__tests__/lexer/amounts.test.ts index a7b9f1c..679d2b1 100644 --- a/src/__tests__/lexer/amounts.test.ts +++ b/src/__tests__/lexer/amounts.test.ts @@ -1,6 +1,6 @@ import test from 'ava'; -import { runLexerTests } from './utils'; +import { macro } from './utils'; const getExpectedOutput = (expected: unknown[]): unknown[] => { return [ @@ -17,285 +17,291 @@ const getExpectedOutput = (expected: unknown[]): unknown[] => { ]; }; -const tests = [ - { - pattern: `1900/01/01 transaction - Assets:Main Chequing 1000 - Expenses:Electronics\n`, - expected: getExpectedOutput(['Number']), - title: 'recognizes amounts as whole numbers' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing 1000.00 - Expenses:Electronics\n`, - expected: getExpectedOutput(['Number']), - title: 'recognizes amounts with decimal values' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing -1000 - Expenses:Electronics\n`, - expected: getExpectedOutput(['DASH', 'Number']), - title: 'recognizes negative whole number amounts' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing -1000.00 - Expenses:Electronics\n`, - expected: getExpectedOutput(['DASH', 'Number']), - title: 'recognizes negative amounts with decimal values' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing +1000 - Expenses:Electronics\n`, - expected: getExpectedOutput(['PLUS', 'Number']), - title: 'recognizes explicitly positive amounts' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([{ CommodityText: '$' }, 'Number']), - title: 'recognizes amounts with a prefixed commodity symbol' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing 1000$ - Expenses:Electronics\n`, - expected: getExpectedOutput(['Number', { CommodityText: '$' }]), - title: 'recognizes amounts with a suffixed commodity symbol' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $ 1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - { CommodityText: '$' }, - 'AMOUNT_WS', - 'Number' - ]), - title: - 'recognizes amounts with a prefixed and space separated commodity symbol' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing 1000 $ - Expenses:Electronics\n`, - expected: getExpectedOutput([ - 'Number', - 'AMOUNT_WS', - { CommodityText: '$' } - ]), - title: - 'recognizes amounts with a suffixed and space separated commodity symbol' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $-1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([{ CommodityText: '$' }, 'DASH', 'Number']), - title: 'recognizes amounts with a dash between symbol and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $- 1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - { CommodityText: '$' }, - 'DASH', - 'AMOUNT_WS', - 'Number' - ]), - title: - 'recognizes amounts with a dash and suffixed space between symbol and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $ -1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - { CommodityText: '$' }, - 'AMOUNT_WS', - 'DASH', - 'Number' - ]), - title: - 'recognizes amounts with a dash and prefixed space between symbol and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $ - 1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - { CommodityText: '$' }, - 'AMOUNT_WS', - 'DASH', - 'AMOUNT_WS', - 'Number' - ]), - title: - 'recognizes amounts with a space-wrapped dash between symbol and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing -$1000 - Expenses:Electronics\n`, - expected: getExpectedOutput(['DASH', { CommodityText: '$' }, 'Number']), - title: 'recognizes amounts with a dash, symbol and number, no spaces' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing - $1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - 'DASH', - 'AMOUNT_WS', - { CommodityText: '$' }, - 'Number' - ]), - title: - 'recognizes amounts with a dash and space preceding the symbol and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing -$ 1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - 'DASH', - { CommodityText: '$' }, - 'AMOUNT_WS', - 'Number' - ]), - title: - 'recognizes amounts with a dash and symbol preceding a space and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing - $ 1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - 'DASH', - 'AMOUNT_WS', - { CommodityText: '$' }, - 'AMOUNT_WS', - 'Number' - ]), - title: - 'recognizes amounts with a dash, symbol and number, all space separated' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing +$1000 - Expenses:Electronics\n`, - expected: getExpectedOutput(['PLUS', { CommodityText: '$' }, 'Number']), - title: 'recognizes amounts with a plus, symbol and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $+1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([{ CommodityText: '$' }, 'PLUS', 'Number']), - title: 'recognizes amounts with a symbol, plus and number' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing + $ 1000 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - 'PLUS', - 'AMOUNT_WS', - { CommodityText: '$' }, - 'AMOUNT_WS', - 'Number' - ]), - title: - 'recognizes amounts with a plus, symbol and number, all space separated' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing -$1.000,00 - Expenses:Electronics\n`, - expected: getExpectedOutput(['DASH', { CommodityText: '$' }, 'Number']), - title: 'recognizes amounts with a comma as amount decimal mark' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing -$1 000.00 - Expenses:Electronics\n`, - expected: getExpectedOutput(['DASH', { CommodityText: '$' }, 'Number']), - title: 'recognizes amounts with a space as amount digit group mark' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing -1,00,00,000.00 INR - Expenses:Electronics\n`, - expected: getExpectedOutput([ - 'DASH', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'INR' } - ]), - title: 'recognizes amounts with alternate digit groupings' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing 1.00EUR - Expenses:Electronics\n`, - expected: getExpectedOutput(['Number', { CommodityText: 'EUR' }]), - title: - "recognizes amounts with a non-space separated commodity starting with 'E'" - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $1.00 = $100.00 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - { CommodityText: '$' }, - 'Number', - 'AMOUNT_WS', - 'EQUALS', - 'AMOUNT_WS', - { CommodityText: '$' }, - 'Number' - ]), - title: 'recognizes amounts in balance assertions' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing 5 foobar @@ $100.00 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - 'Number', - 'AMOUNT_WS', - { CommodityText: 'foobar' }, - 'AMOUNT_WS', - 'AT', - 'AT', - 'AMOUNT_WS', - { CommodityText: '$' }, - 'Number' - ]), - title: 'recognizes amounts in lot prices' - }, - { - pattern: `1900/01/01 transaction - Assets:Main Chequing $\t1.00\t =\t $\t100.00 - Expenses:Electronics\n`, - expected: getExpectedOutput([ - { CommodityText: '$' }, - 'AMOUNT_WS', - 'Number', - 'AMOUNT_WS', - 'EQUALS', - 'AMOUNT_WS', - { CommodityText: '$' }, - 'AMOUNT_WS', - 'Number' - ]), - title: 'recognizes amounts containing tabs and spaces' - } -]; +test( + 'recognizes amounts as whole numbers', + macro, + `1900/01/01 transaction + Assets:Main Chequing 1000 + Expenses:Electronics\n`, + getExpectedOutput(['Number']) +); -runLexerTests(test, tests); +test( + 'recognizes amounts with decimal values', + macro, + `1900/01/01 transaction + Assets:Main Chequing 1000.00 + Expenses:Electronics\n`, + getExpectedOutput(['Number']) +); + +test( + 'recognizes negative whole number amounts', + macro, + `1900/01/01 transaction + Assets:Main Chequing -1000 + Expenses:Electronics\n`, + getExpectedOutput(['DASH', 'Number']) +); + +test( + 'recognizes negative amounts with decimal values', + macro, + `1900/01/01 transaction + Assets:Main Chequing -1000.00 + Expenses:Electronics\n`, + getExpectedOutput(['DASH', 'Number']) +); + +test( + 'recognizes explicitly positive amounts', + macro, + `1900/01/01 transaction + Assets:Main Chequing +1000 + Expenses:Electronics\n`, + getExpectedOutput(['PLUS', 'Number']) +); + +test( + 'recognizes amounts with a prefixed commodity symbol', + macro, + `1900/01/01 transaction + Assets:Main Chequing $1000 + Expenses:Electronics\n`, + getExpectedOutput([{ CommodityText: '$' }, 'Number']) +); + +test( + 'recognizes amounts with a suffixed commodity symbol', + macro, + `1900/01/01 transaction + Assets:Main Chequing 1000$ + Expenses:Electronics\n`, + getExpectedOutput(['Number', { CommodityText: '$' }]) +); + +test( + 'recognizes amounts with a prefixed and space separated commodity symbol', + macro, + `1900/01/01 transaction + Assets:Main Chequing $ 1000 + Expenses:Electronics\n`, + getExpectedOutput([{ CommodityText: '$' }, 'AMOUNT_WS', 'Number']) +); + +test( + 'recognizes amounts with a suffixed and space separated commodity symbol', + macro, + `1900/01/01 transaction + Assets:Main Chequing 1000 $ + Expenses:Electronics\n`, + getExpectedOutput(['Number', 'AMOUNT_WS', { CommodityText: '$' }]) +); + +test( + 'recognizes amounts with a dash between symbol and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing $-1000 + Expenses:Electronics\n`, + getExpectedOutput([{ CommodityText: '$' }, 'DASH', 'Number']) +); + +test( + 'recognizes amounts with a dash and suffixed space between symbol and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing $- 1000 + Expenses:Electronics\n`, + getExpectedOutput([{ CommodityText: '$' }, 'DASH', 'AMOUNT_WS', 'Number']) +); + +test( + 'recognizes amounts with a dash and prefixed space between symbol and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing $ -1000 + Expenses:Electronics\n`, + getExpectedOutput([{ CommodityText: '$' }, 'AMOUNT_WS', 'DASH', 'Number']) +); + +test( + 'recognizes amounts with a space-wrapped dash between symbol and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing $ - 1000 + Expenses:Electronics\n`, + getExpectedOutput([ + { CommodityText: '$' }, + 'AMOUNT_WS', + 'DASH', + 'AMOUNT_WS', + 'Number' + ]) +); + +test( + 'recognizes amounts with a dash, symbol and number, no spaces', + macro, + `1900/01/01 transaction + Assets:Main Chequing -$1000 + Expenses:Electronics\n`, + getExpectedOutput(['DASH', { CommodityText: '$' }, 'Number']) +); + +test( + 'recognizes amounts with a dash and space preceding the symbol and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing - $1000 + Expenses:Electronics\n`, + getExpectedOutput(['DASH', 'AMOUNT_WS', { CommodityText: '$' }, 'Number']) +); + +test( + 'recognizes amounts with a dash and symbol preceding a space and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing -$ 1000 + Expenses:Electronics\n`, + getExpectedOutput(['DASH', { CommodityText: '$' }, 'AMOUNT_WS', 'Number']) +); + +test( + 'recognizes amounts with a dash, symbol and number, all space separated', + macro, + `1900/01/01 transaction + Assets:Main Chequing - $ 1000 + Expenses:Electronics\n`, + getExpectedOutput([ + 'DASH', + 'AMOUNT_WS', + { CommodityText: '$' }, + 'AMOUNT_WS', + 'Number' + ]) +); + +test( + 'recognizes amounts with a plus, symbol and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing +$1000 + Expenses:Electronics\n`, + getExpectedOutput(['PLUS', { CommodityText: '$' }, 'Number']) +); + +test( + 'recognizes amounts with a symbol, plus and number', + macro, + `1900/01/01 transaction + Assets:Main Chequing $+1000 + Expenses:Electronics\n`, + getExpectedOutput([{ CommodityText: '$' }, 'PLUS', 'Number']) +); + +test( + 'recognizes amounts with a plus, symbol and number, all space separated', + macro, + `1900/01/01 transaction + Assets:Main Chequing + $ 1000 + Expenses:Electronics\n`, + getExpectedOutput([ + 'PLUS', + 'AMOUNT_WS', + { CommodityText: '$' }, + 'AMOUNT_WS', + 'Number' + ]) +); + +test( + 'recognizes amounts with a comma as amount decimal mark', + macro, + `1900/01/01 transaction + Assets:Main Chequing -$1.000,00 + Expenses:Electronics\n`, + getExpectedOutput(['DASH', { CommodityText: '$' }, 'Number']) +); + +test( + 'recognizes amounts with a space as amount digit group mark', + macro, + `1900/01/01 transaction + Assets:Main Chequing -$1 000.00 + Expenses:Electronics\n`, + getExpectedOutput(['DASH', { CommodityText: '$' }, 'Number']) +); + +test( + 'recognizes amounts with alternate digit groupings', + macro, + `1900/01/01 transaction + Assets:Main Chequing -1,00,00,000.00 INR + Expenses:Electronics\n`, + getExpectedOutput(['DASH', 'Number', 'AMOUNT_WS', { CommodityText: 'INR' }]) +); + +test( + "recognizes amounts with a non-space separated commodity starting with 'E'", + macro, + `1900/01/01 transaction + Assets:Main Chequing 1.00EUR + Expenses:Electronics\n`, + getExpectedOutput(['Number', { CommodityText: 'EUR' }]) +); + +test( + 'recognizes amounts in balance assertions', + macro, + `1900/01/01 transaction + Assets:Main Chequing $1.00 = $100.00 + Expenses:Electronics\n`, + getExpectedOutput([ + { CommodityText: '$' }, + 'Number', + 'AMOUNT_WS', + 'EQUALS', + 'AMOUNT_WS', + { CommodityText: '$' }, + 'Number' + ]) +); + +test( + 'recognizes amounts in lot prices', + macro, + `1900/01/01 transaction + Assets:Main Chequing 5 foobar @@ $100.00 + Expenses:Electronics\n`, + getExpectedOutput([ + 'Number', + 'AMOUNT_WS', + { CommodityText: 'foobar' }, + 'AMOUNT_WS', + 'AT', + 'AT', + 'AMOUNT_WS', + { CommodityText: '$' }, + 'Number' + ]) +); + +test( + 'recognizes amounts containing tabs and spaces', + macro, + `1900/01/01 transaction + Assets:Main Chequing $\t1.00\t =\t $\t100.00 + Expenses:Electronics\n`, + getExpectedOutput([ + { CommodityText: '$' }, + 'AMOUNT_WS', + 'Number', + 'AMOUNT_WS', + 'EQUALS', + 'AMOUNT_WS', + { CommodityText: '$' }, + 'AMOUNT_WS', + 'Number' + ]) +); diff --git a/src/__tests__/lexer/commodity_directives.test.ts b/src/__tests__/lexer/commodity_directives.test.ts index e59fe72..9253950 100644 --- a/src/__tests__/lexer/commodity_directives.test.ts +++ b/src/__tests__/lexer/commodity_directives.test.ts @@ -1,232 +1,230 @@ import test from 'ava'; -import { runLexerTests } from './utils'; - -const tests = [ - { - pattern: 'commodity USD', - expected: ['CommodityDirective', { CommodityText: 'USD' }], - title: 'recognizes a simple commodity directive' - }, - { - pattern: 'commodity USD\n', - expected: ['CommodityDirective', { CommodityText: 'USD' }, 'NEWLINE'], - title: 'recognizes a simple commodity directive with end of line at end' - }, - { - pattern: 'commodity $1000.00', - expected: ['CommodityDirective', { CommodityText: '$' }, 'Number'], - title: 'recognizes a commodity directive with inline format definition' - }, - { - pattern: 'commodity $1000', - expected: ['CommodityDirective', { CommodityText: '$' }, 'Number'], - title: - 'recognizes a commodity directive where inline format lacks disambiguating decimal mark' - }, - { - pattern: 'commodity $1,000.00', - expected: ['CommodityDirective', { CommodityText: '$' }, 'Number'], - title: - 'recognizes a commodity directive where inline format has digit group mark' - }, - { - pattern: 'commodity 1000.00 USD', - expected: [ - 'CommodityDirective', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'USD' } - ], - title: - 'recognizes a commodity directive where commodity text is suffixed and space-separated from number' - }, - { - pattern: 'commodity USD 1000.00', - expected: [ - 'CommodityDirective', - { CommodityText: 'USD' }, - 'AMOUNT_WS', - 'Number' - ], - title: - 'recognizes a commodity directive where commodity text is prefixed and space-separated from number' - }, - { - pattern: 'commodity $1,000.00 ; comment\n', - expected: [ - 'CommodityDirective', - { CommodityText: '$' }, - 'Number', - 'AMOUNT_WS', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE' - ], - title: - 'recognizes a commodity directive with inline format and an inline comment' - }, - { - pattern: `commodity CAD - format 1000.00 CAD`, - expected: [ - 'CommodityDirective', - { CommodityText: 'CAD' }, - 'NEWLINE', - 'INDENT', - 'FormatSubdirective', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'CAD' } - ], - title: 'recognizes a format subdirective' - }, - { - pattern: `commodity CAD - format 1000.00 CAD ; comment\n`, - expected: [ - 'CommodityDirective', - { CommodityText: 'CAD' }, - 'NEWLINE', - 'INDENT', - 'FormatSubdirective', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'CAD' }, - 'AMOUNT_WS', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE' - ], - title: 'recognizes a format subdirective with an inline comment' - }, - { - pattern: `commodity CAD - format 1000.00 CAD - ; comment\n`, - expected: [ - 'CommodityDirective', - { CommodityText: 'CAD' }, - 'NEWLINE', - 'INDENT', - 'FormatSubdirective', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'CAD' }, - 'NEWLINE', - 'INDENT', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE' - ], - title: - 'recognizes a commodity directive with format subdirective and a subdirective comment' - }, - { - pattern: `commodity CAD - ; comment - format 1000.00 CAD`, - expected: [ - 'CommodityDirective', - { CommodityText: 'CAD' }, - 'NEWLINE', - 'INDENT', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE', - 'INDENT', - 'FormatSubdirective', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'CAD' } - ], - title: - 'recognizes a commodity directive with subdirective comment preceding format subdirective' - }, - { - pattern: `commodity CAD - ; comment - ; comment - format 1000.00 CAD - ; comment\n`, - expected: [ - 'CommodityDirective', - { CommodityText: 'CAD' }, - 'NEWLINE', - 'INDENT', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE', - 'INDENT', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE', - 'INDENT', - 'FormatSubdirective', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'CAD' }, - 'NEWLINE', - 'INDENT', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE' - ], - title: - 'recognizes a commodity directive with multiple subdirective comments and a format subdirective' - }, - { - pattern: 'D $1000.00', - expected: ['DefaultCommodityDirective', { CommodityText: '$' }, 'Number'], - title: 'recognizes a default commodity directive' - }, - { - pattern: 'D $1000', - expected: ['DefaultCommodityDirective', { CommodityText: '$' }, 'Number'], - title: - 'recognizes a default commodity directive where currency format lacks disambiguating decimal mark' - }, - { - pattern: 'D $1,000.00', - expected: ['DefaultCommodityDirective', { CommodityText: '$' }, 'Number'], - title: - 'recognizes a default commodity directive where currency format has digit group mark' - }, - { - pattern: 'D 1000.00 USD', - expected: [ - 'DefaultCommodityDirective', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'USD' } - ], - title: - 'recognizes a default commodity directive where commodity text is suffixed and space-separated from number' - }, - { - pattern: 'D USD 1000.00', - expected: [ - 'DefaultCommodityDirective', - { CommodityText: 'USD' }, - 'AMOUNT_WS', - 'Number' - ], - title: - 'recognizes a default commodity directive where commodity text is prefixed and space-separated from number' - }, - { - pattern: 'D $1,000.00 ; comment\n', - expected: [ - 'DefaultCommodityDirective', - { CommodityText: '$' }, - 'Number', - 'AMOUNT_WS', - 'SemicolonComment', - 'InlineCommentText', - 'NEWLINE' - ], - title: 'recognizes a default commodity directive with an inline comment' - } -]; - -runLexerTests(test, tests); +import { macro } from './utils'; + +test('recognizes a simple commodity directive', macro, 'commodity USD', [ + 'CommodityDirective', + { CommodityText: 'USD' } +]); + +test( + 'recognizes a simple commodity directive with end of line at end', + macro, + 'commodity USD\n', + ['CommodityDirective', { CommodityText: 'USD' }, 'NEWLINE'] +); + +test( + 'recognizes a commodity directive with inline format definition', + macro, + 'commodity $1000.00', + ['CommodityDirective', { CommodityText: '$' }, 'Number'] +); + +test( + 'recognizes a commodity directive where inline format lacks disambiguating decimal mark', + macro, + 'commodity $1000', + ['CommodityDirective', { CommodityText: '$' }, 'Number'] +); + +test( + 'recognizes a commodity directive where inline format has digit group mark', + macro, + 'commodity $1,000.00', + ['CommodityDirective', { CommodityText: '$' }, 'Number'] +); + +test( + 'recognizes a commodity directive where commodity text is suffixed and space-separated from number', + macro, + 'commodity 1000.00 USD', + ['CommodityDirective', 'Number', 'AMOUNT_WS', { CommodityText: 'USD' }] +); + +test( + 'recognizes a commodity directive where commodity text is prefixed and space-separated from number', + macro, + 'commodity USD 1000.00', + ['CommodityDirective', { CommodityText: 'USD' }, 'AMOUNT_WS', 'Number'] +); + +test( + 'recognizes a commodity directive with inline format and an inline comment', + macro, + 'commodity $1,000.00 ; comment\n', + [ + 'CommodityDirective', + { CommodityText: '$' }, + 'Number', + 'AMOUNT_WS', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test( + 'recognizes a format subdirective', + macro, + `commodity CAD + format 1000.00 CAD`, + [ + 'CommodityDirective', + { CommodityText: 'CAD' }, + 'NEWLINE', + 'INDENT', + 'FormatSubdirective', + 'Number', + 'AMOUNT_WS', + { CommodityText: 'CAD' } + ] +); + +test( + 'recognizes a format subdirective with an inline comment', + macro, + `commodity CAD + format 1000.00 CAD ; comment\n`, + [ + 'CommodityDirective', + { CommodityText: 'CAD' }, + 'NEWLINE', + 'INDENT', + 'FormatSubdirective', + 'Number', + 'AMOUNT_WS', + { CommodityText: 'CAD' }, + 'AMOUNT_WS', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test( + 'recognizes a commodity directive with format subdirective and a subdirective comment', + macro, + `commodity CAD + format 1000.00 CAD + ; comment\n`, + [ + 'CommodityDirective', + { CommodityText: 'CAD' }, + 'NEWLINE', + 'INDENT', + 'FormatSubdirective', + 'Number', + 'AMOUNT_WS', + { CommodityText: 'CAD' }, + 'NEWLINE', + 'INDENT', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test( + 'recognizes a commodity directive with subdirective comment preceding format subdirective', + macro, + `commodity CAD + ; comment + format 1000.00 CAD`, + [ + 'CommodityDirective', + { CommodityText: 'CAD' }, + 'NEWLINE', + 'INDENT', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE', + 'INDENT', + 'FormatSubdirective', + 'Number', + 'AMOUNT_WS', + { CommodityText: 'CAD' } + ] +); + +test( + 'recognizes a commodity directive with multiple subdirective comments and a format subdirective', + macro, + `commodity CAD + ; comment + ; comment + format 1000.00 CAD + ; comment\n`, + [ + 'CommodityDirective', + { CommodityText: 'CAD' }, + 'NEWLINE', + 'INDENT', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE', + 'INDENT', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE', + 'INDENT', + 'FormatSubdirective', + 'Number', + 'AMOUNT_WS', + { CommodityText: 'CAD' }, + 'NEWLINE', + 'INDENT', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test('recognizes a default commodity directive', macro, 'D $1000.00', [ + 'DefaultCommodityDirective', + { CommodityText: '$' }, + 'Number' +]); + +test( + 'recognizes a default commodity directive where currency format lacks disambiguating decimal mark', + macro, + 'D $1000', + ['DefaultCommodityDirective', { CommodityText: '$' }, 'Number'] +); + +test( + 'recognizes a default commodity directive where currency format has digit group mark', + macro, + 'D $1,000.00', + ['DefaultCommodityDirective', { CommodityText: '$' }, 'Number'] +); + +test( + 'recognizes a default commodity directive where commodity text is suffixed and space-separated from number', + macro, + 'D 1000.00 USD', + ['DefaultCommodityDirective', 'Number', 'AMOUNT_WS', { CommodityText: 'USD' }] +); + +test( + 'recognizes a default commodity directive where commodity text is prefixed and space-separated from number', + macro, + 'D USD 1000.00', + ['DefaultCommodityDirective', { CommodityText: 'USD' }, 'AMOUNT_WS', 'Number'] +); + +test( + 'recognizes a default commodity directive with an inline comment', + macro, + 'D $1,000.00 ; comment\n', + [ + 'DefaultCommodityDirective', + { CommodityText: '$' }, + 'Number', + 'AMOUNT_WS', + 'SemicolonComment', + 'InlineCommentText', + 'NEWLINE' + ] +); diff --git a/src/__tests__/lexer/full_line_comments.test.ts b/src/__tests__/lexer/full_line_comments.test.ts index b8ecc69..88933a3 100644 --- a/src/__tests__/lexer/full_line_comments.test.ts +++ b/src/__tests__/lexer/full_line_comments.test.ts @@ -1,28 +1,25 @@ import test from 'ava'; -import { runLexerTests } from './utils'; +import { macro } from './utils'; -const tests = [ - { - pattern: '# a comment', - expected: ['HASHTAG_AT_START', 'CommentText'], - title: 'recognize full-line comments starting with #' - }, - { - pattern: '; a comment', - expected: ['SEMICOLON_AT_START', 'CommentText'], - title: 'recognize full-line comments starting with ;' - }, - { - pattern: '* a comment', - expected: ['ASTERISK_AT_START', 'CommentText'], - title: 'recognize full-line comments starting with *' - }, - { - pattern: '# a comment not-a-tag: not a tag value', - expected: ['HASHTAG_AT_START', 'CommentText'], - title: 'does not recognize tags in full-line comments' - } -]; +test('recognize full-line comments starting with #', macro, '# a comment', [ + 'HASHTAG_AT_START', + 'CommentText' +]); -runLexerTests(test, tests); +test('recognize full-line comments starting with ;', macro, '; a comment', [ + 'SEMICOLON_AT_START', + 'CommentText' +]); + +test('recognize full-line comments starting with *', macro, '* a comment', [ + 'ASTERISK_AT_START', + 'CommentText' +]); + +test( + 'does not recognize tags in full-line comments', + macro, + '# a comment not-a-tag: not a tag value', + ['HASHTAG_AT_START', 'CommentText'] +); diff --git a/src/__tests__/lexer/multiline_comments.test.ts b/src/__tests__/lexer/multiline_comments.test.ts index f947f14..406033d 100644 --- a/src/__tests__/lexer/multiline_comments.test.ts +++ b/src/__tests__/lexer/multiline_comments.test.ts @@ -1,95 +1,91 @@ import test from 'ava'; -import { runLexerTests } from './utils'; +import { macro } from './utils'; -const tests = [ - { - pattern: 'comment', - expected: ['MultilineComment'], - title: 'recognize multiline comment' - }, - { - pattern: `comment +test('recognize multiline comment', macro, 'comment', ['MultilineComment']); + +test( + 'recognize multiline comment terminator', + macro, + `comment end comment `, - expected: [ - 'MultilineComment', - 'MC_NEWLINE', - 'MultilineCommentEnd', - 'NEWLINE' - ], - title: 'recognize multiline comment terminator' - }, - { - pattern: `comment + ['MultilineComment', 'MC_NEWLINE', 'MultilineCommentEnd', 'NEWLINE'] +); + +test( + 'recognize multiline comment text', + macro, + `comment This is a multiline comment end comment `, - expected: [ - 'MultilineComment', - 'MC_NEWLINE', - 'MultilineCommentText', - 'MC_NEWLINE', - 'MultilineCommentEnd', - 'NEWLINE' - ], - title: 'recognize multiline comment text' - }, - { - pattern: `comment + [ + 'MultilineComment', + 'MC_NEWLINE', + 'MultilineCommentText', + 'MC_NEWLINE', + 'MultilineCommentEnd', + 'NEWLINE' + ] +); + +test( + 'recognize several lines of multiline comment text', + macro, + `comment This is a multiline comment This is a multiline comment This is a multiline comment This is a multiline comment end comment `, - expected: [ - 'MultilineComment', - 'MC_NEWLINE', - 'MultilineCommentText', - 'MC_NEWLINE', - 'MultilineCommentText', - 'MC_NEWLINE', - 'MultilineCommentText', - 'MC_NEWLINE', - 'MultilineCommentText', - 'MC_NEWLINE', - 'MultilineCommentEnd', - 'NEWLINE' - ], - title: 'recognize several lines of multiline comment text' - }, - { - pattern: `comment + [ + 'MultilineComment', + 'MC_NEWLINE', + 'MultilineCommentText', + 'MC_NEWLINE', + 'MultilineCommentText', + 'MC_NEWLINE', + 'MultilineCommentText', + 'MC_NEWLINE', + 'MultilineCommentText', + 'MC_NEWLINE', + 'MultilineCommentEnd', + 'NEWLINE' + ] +); + +test( + 'does not recognize tags in multiline comments', + macro, + `comment This is a multiline comment with not-a-tag: and not a value end comment `, - expected: [ - 'MultilineComment', - 'MC_NEWLINE', - 'MultilineCommentText', - 'MC_NEWLINE', - 'MultilineCommentEnd', - 'NEWLINE' - ], - title: 'does not recognize tags in multiline comments' - }, - { - pattern: `comment + [ + 'MultilineComment', + 'MC_NEWLINE', + 'MultilineCommentText', + 'MC_NEWLINE', + 'MultilineCommentEnd', + 'NEWLINE' + ] +); + +test( + 'only recognizes multiline comment terminator at the beginning of a line', + macro, + `comment This is a multiline comment with end comment in the text end comment `, - expected: [ - 'MultilineComment', - 'MC_NEWLINE', - 'MultilineCommentText', - 'MC_NEWLINE', - 'MultilineCommentEnd', - 'NEWLINE' - ], - title: - 'only recognizes multiline comment terminator at the beginning of a line' - } -]; - -runLexerTests(test, tests); + [ + 'MultilineComment', + 'MC_NEWLINE', + 'MultilineCommentText', + 'MC_NEWLINE', + 'MultilineCommentEnd', + 'NEWLINE' + ] +); diff --git a/src/__tests__/lexer/price_directives.test.ts b/src/__tests__/lexer/price_directives.test.ts index 774d365..a0f14c2 100644 --- a/src/__tests__/lexer/price_directives.test.ts +++ b/src/__tests__/lexer/price_directives.test.ts @@ -1,141 +1,142 @@ import test from 'ava'; -import { runLexerTests } from './utils'; +import { macro } from './utils'; -const tests = [ - { - pattern: 'P 1900/01/01 $ 1 CAD', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: '$' }, - 'Number', - 'AMOUNT_WS', - { CommodityText: 'CAD' } - ], - title: 'recognize currency and alphanumeric commodities' - }, - { - pattern: 'P 1900/01/01 $US 1 CAD', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: '$US' }, - 'Number', - 'AMOUNT_WS', - { CommodityText: 'CAD' } - ], - title: - 'recognize commodities that are a mix of alphanumeric and currency symbols' - }, - { - pattern: 'P 1900/01/01 USD $1', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - { CommodityText: '$' }, - 'Number' - ], - title: 'recognize commodities preceding a price number' - }, - { - pattern: 'P 1900/01/01 USD -$1', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - 'DASH', - { CommodityText: '$' }, - 'Number' - ], - title: - 'recognize commodities preceding a price number with a negative in front of the commodity' - }, - { - pattern: 'P 1900/01/01 USD +$1', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - 'PLUS', - { CommodityText: '$' }, - 'Number' - ], - title: - 'recognize commodities preceding a price number with a positive in front of the commodity' - }, - { - pattern: 'P 1900/01/01 USD $1.199', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - { CommodityText: '$' }, - 'Number' - ], - title: 'recognize numbers with a decimal' - }, - { - pattern: 'P 1900/01/01 USD $1,199', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - { CommodityText: '$' }, - 'Number' - ], - title: 'recognize numbers with a comma' - }, - { - pattern: 'P 1900/01/01 USD $1,199.02', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - { CommodityText: '$' }, - 'Number' - ], - title: 'recognize numbers with a comma and a decimal' - }, - { - pattern: 'P 01/01 USD $1,199.02', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - { CommodityText: '$' }, - 'Number' - ], - title: 'recognize date without a year' - }, - { - pattern: 'P 1900/1/01 USD $1,199.02', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - { CommodityText: '$' }, - 'Number' - ], - title: 'recognize date with only 1 month digit' - }, - { - pattern: 'P 1900/01/1 USD $1,199.02', - expected: [ - 'PDirective', - 'SimpleDate', - { PDirectiveCommodityText: 'USD' }, - { CommodityText: '$' }, - 'Number' - ], - title: 'recognize date with only 1 date digit' - }, - { - pattern: 'p 1900/01/01 USD -$1', - expected: [], - title: 'reject lowercase p for price directive' - } -]; +test( + 'recognize currency and alphanumeric commodities', + macro, + 'P 1900/01/01 $ 1 CAD', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: '$' }, + 'Number', + 'AMOUNT_WS', + { CommodityText: 'CAD' } + ] +); -runLexerTests(test, tests); +test( + 'recognize commodities that are a mix of alphanumeric and currency symbols', + macro, + 'P 1900/01/01 $US 1 CAD', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: '$US' }, + 'Number', + 'AMOUNT_WS', + { CommodityText: 'CAD' } + ] +); + +test( + 'recognize commodities preceding a price number', + macro, + 'P 1900/01/01 USD $1', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + { CommodityText: '$' }, + 'Number' + ] +); + +test( + 'recognize commodities preceding a price number with a negative in front of the commodity', + macro, + 'P 1900/01/01 USD -$1', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + 'DASH', + { CommodityText: '$' }, + 'Number' + ] +); + +test( + 'recognize commodities preceding a price number with a positive in front of the commodity', + macro, + 'P 1900/01/01 USD +$1', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + 'PLUS', + { CommodityText: '$' }, + 'Number' + ] +); + +test('recognize numbers with a decimal', macro, 'P 1900/01/01 USD $1.199', [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + { CommodityText: '$' }, + 'Number' +]); + +test('recognize numbers with a comma', macro, 'P 1900/01/01 USD $1,199', [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + { CommodityText: '$' }, + 'Number' +]); + +test( + 'recognize numbers with a comma and a decimal', + macro, + 'P 1900/01/01 USD $1,199.02', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + { CommodityText: '$' }, + 'Number' + ] +); + +test('recognize date without a year', macro, 'P 01/01 USD $1,199.02', [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + { CommodityText: '$' }, + 'Number' +]); + +test( + 'recognize date with only 1 month digit', + macro, + 'P 1900/1/01 USD $1,199.02', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + { CommodityText: '$' }, + 'Number' + ] +); + +test( + 'recognize date with only 1 date digit', + macro, + 'P 1900/01/1 USD $1,199.02', + [ + 'PDirective', + 'SimpleDate', + { PDirectiveCommodityText: 'USD' }, + { CommodityText: '$' }, + 'Number' + ] +); + +test( + 'reject lowercase p for price directive', + macro, + 'p 1900/01/01 USD -$1', + [] +); diff --git a/src/__tests__/lexer/start_of_line_directives.test.ts b/src/__tests__/lexer/start_of_line_directives.test.ts index 87a81a0..0393c04 100644 --- a/src/__tests__/lexer/start_of_line_directives.test.ts +++ b/src/__tests__/lexer/start_of_line_directives.test.ts @@ -1,14 +1,12 @@ import test from 'ava'; -import { runLexerTests } from './utils'; +import { macro } from './utils'; -const tests = [ - { - pattern: 'An Account:Test', - expected: [], - title: `reject a line starting with anything other than date, indent, ;, #, *, 'account', or 'P'` - } - // TODO: Write tests for each start of line directive. -]; +test( + "reject a line starting with anything other than date, indent, ;, #, *, 'account', or 'P'", + macro, + 'An Account:Test', + [] +); -runLexerTests(test, tests); +// TODO: Write tests for each start of line directive. diff --git a/src/__tests__/lexer/tags.test.ts b/src/__tests__/lexer/tags.test.ts index d3d320d..d6c3a66 100644 --- a/src/__tests__/lexer/tags.test.ts +++ b/src/__tests__/lexer/tags.test.ts @@ -1,255 +1,262 @@ import test from 'ava'; -import { runLexerTests } from './utils'; - -const tests = [ - { - pattern: 'account Assets ; tag:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes a tag' - }, - { - pattern: 'account Assets ; tag1:,tag2:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagComma', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes multiple tags' - }, - { - pattern: 'account Assets ; tag: value\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'NEWLINE' - ], - title: 'recognizes a tag with a value' - }, - { - pattern: 'account Assets ; tag1: value1,tag2:value2\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'InlineCommentTagComma', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'NEWLINE' - ], - title: 'recognizes multiple tags with values' - }, - { - pattern: 'account Assets ; comment tag:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes a comment preceding a tag' - }, - { - pattern: 'account Assets ; tag:, comment\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagComma', - 'InlineCommentText', - 'NEWLINE' - ], - title: 'recognizes a tag preceding a comment' - }, - { - pattern: 'account Assets ; tag: value, comment\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'InlineCommentTagComma', - 'InlineCommentText', - 'NEWLINE' - ], - title: 'recognizes a tag with a value preceding a comment' - }, - { - pattern: 'account Assets ; tag1:, comment tag2:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagComma', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes a comment between two tags' - }, - { - pattern: 'account Assets ; :tag:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'ignores colon preceding a tag' - }, - { - pattern: 'account Assets ; :tag1:tag2:tag3:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'NEWLINE' - ], - title: 'recognizes a tag value containing colons' - }, - { - pattern: 'account Assets ; ~`!@#$%^&*()_-+={},[]\\|"\'.<;>tag1:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes a tag name containing unusual characters' - }, - { - pattern: 'account Assets ; 标签:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: - 'recognizes a tag name containing Unicode characters (Simplified Chinese)' - }, - { - pattern: 'account Assets ; بطاقةشعار:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes a tag name containing Unicode characters (Arabic)' - }, - { - pattern: 'account Assets ; תָג:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes a tag name containing Unicode characters (Hebrew)' - }, - { - pattern: 'account Assets ; Тег:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: 'recognizes a tag name containing Unicode characters (Ukrainian)' - }, - { - pattern: 'account Assets ; ,:value\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'NEWLINE' - ], - title: 'recognizes a tag name consisting of a single comma' - }, - { - pattern: 'account Assets ; ::::::tag1:\n', - expected: [ - 'AccountDirective', - { AccountName: ['Assets'] }, - 'DOUBLE_WS', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'NEWLINE' - ], - title: - 'recognizes comment text consisting of repeated colons preceding a tag' - } -]; - -runLexerTests(test, tests); +import { macro } from './utils'; + +test('recognizes a tag', macro, 'account Assets ; tag:\n', [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' +]); + +test('recognizes multiple tags', macro, 'account Assets ; tag1:,tag2:\n', [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagComma', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' +]); + +test('recognizes a tag with a value', macro, 'account Assets ; tag: value\n', [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'NEWLINE' +]); + +test( + 'recognizes multiple tags with values', + macro, + 'account Assets ; tag1: value1,tag2:value2\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'InlineCommentTagComma', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'NEWLINE' + ] +); + +test( + 'recognizes a comment preceding a tag', + macro, + 'account Assets ; comment tag:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag preceding a comment', + macro, + 'account Assets ; tag:, comment\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagComma', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag with a value preceding a comment', + macro, + 'account Assets ; tag: value, comment\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'InlineCommentTagComma', + 'InlineCommentText', + 'NEWLINE' + ] +); + +test( + 'recognizes a comment between two tags', + macro, + 'account Assets ; tag1:, comment tag2:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagComma', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); + +test('ignores colon preceding a tag', macro, 'account Assets ; :tag:\n', [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' +]); + +test( + 'recognizes a tag value containing colons', + macro, + 'account Assets ; :tag1:tag2:tag3:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag name containing unusual characters', + macro, + 'account Assets ; ~`!@#$%^&*()_-+={},[]\\|"\'.<;>tag1:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag name containing Unicode characters (Simplified Chinese)', + macro, + 'account Assets ; 标签:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag name containing Unicode characters (Arabic)', + macro, + 'account Assets ; بطاقةشعار:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag name containing Unicode characters (Hebrew)', + macro, + 'account Assets ; תָג:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag name containing Unicode characters (Ukrainian)', + macro, + 'account Assets ; Тег:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); + +test( + 'recognizes a tag name consisting of a single comma', + macro, + 'account Assets ; ,:value\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'NEWLINE' + ] +); + +test( + 'recognizes comment text consisting of repeated colons preceding a tag', + macro, + 'account Assets ; ::::::tag1:\n', + [ + 'AccountDirective', + { AccountName: ['Assets'] }, + 'DOUBLE_WS', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'NEWLINE' + ] +); diff --git a/src/__tests__/lexer/transactions.test.ts b/src/__tests__/lexer/transactions.test.ts index c7f86d9..5645498 100644 --- a/src/__tests__/lexer/transactions.test.ts +++ b/src/__tests__/lexer/transactions.test.ts @@ -1,142 +1,144 @@ import test from 'ava'; -import { runLexerTests } from './utils'; +import { macro } from './utils'; -const tests = [ - { - pattern: '01/01\n', - expected: ['DateAtStart', 'NEWLINE'], - title: 'recognize a minimal transaction' - }, - { - pattern: `1900/01/01 New York Steakhouse - Assets:Main Chequing -$23.05 = $100.00 - Expenses:Food\n`, - expected: [ - 'DateAtStart', - 'Text', - 'NEWLINE', - 'INDENT', - { RealAccountName: ['Assets', 'Main Chequing'] }, - 'DASH', - { CommodityText: '$' }, - 'Number', - 'AMOUNT_WS', - 'EQUALS', - 'AMOUNT_WS', - { CommodityText: '$' }, - 'Number', - 'NEWLINE', - 'INDENT', - { RealAccountName: ['Expenses', 'Food'] }, - 'NEWLINE' - ], - title: 'recognize a realistic transaction' - }, - { - pattern: `1900/01/01 * (a) payee|memo ; comment and tag: value, - Assets:Real -$1.00 @ 20 "green apples" = -$1.00 - (Assets:Virtual) 2CAD @@ 20$ == 0.1 - [Assets:VirtualBal:Sub] 1.3e4 (@@) 0.4 *== 1 ; comment tag1: val1, tag2: val2 - ; another comment with a tag:value\n`, - expected: [ - 'DateAtStart', - 'TxnStatusIndicator', - { ParenValue: 'a' }, - 'Text', - 'PIPE', - 'Memo', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'InlineCommentTagComma', - 'NEWLINE', - 'INDENT', - { RealAccountName: ['Assets', 'Real'] }, - 'DASH', - { CommodityText: '$' }, - 'Number', - 'AMOUNT_WS', - 'AT', - 'AMOUNT_WS', - 'Number', - 'AMOUNT_WS', - { CommodityText: 'green apples' }, - 'AMOUNT_WS', - 'EQUALS', - 'AMOUNT_WS', - 'DASH', - { CommodityText: '$' }, - 'Number', - 'NEWLINE', - 'INDENT', - { VirtualAccountName: ['Assets', 'Virtual'] }, - 'Number', - { CommodityText: 'CAD' }, - 'AMOUNT_WS', - 'AT', - 'AT', - 'AMOUNT_WS', - 'Number', - { CommodityText: '$' }, - 'AMOUNT_WS', - 'EQUALS', - 'EQUALS', - 'AMOUNT_WS', - 'Number', - 'NEWLINE', - 'INDENT', - { VirtualBalancedAccountName: ['Assets', 'VirtualBal', 'Sub'] }, - 'Number', - 'AMOUNT_WS', - 'LPAREN', - 'AT', - 'AT', - 'RPAREN', - 'AMOUNT_WS', - 'Number', - 'AMOUNT_WS', - 'ASTERISK', - 'EQUALS', - 'EQUALS', - 'AMOUNT_WS', - 'Number', - 'AMOUNT_WS', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'InlineCommentTagComma', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'NEWLINE', - 'INDENT', - 'SemicolonComment', - 'InlineCommentText', - 'InlineCommentTagName', - 'InlineCommentTagColon', - 'InlineCommentTagValue', - 'NEWLINE' - ], - title: 'recognize a maximal transaction' - }, - { - pattern: '1900/01/01 New York Steakhouse|memo|note|something else|', - expected: ['DateAtStart', 'Text', 'PIPE', 'Memo'], - title: - 'recognize a transaction with a payee note containing pipe characters' - }, - { - pattern: '2023-02-29 Invalid transaction', - expected: [], - title: 'does not recognize an invalid transaction date' - } -]; +test('recognize a minimal transaction', macro, '01/01\n', [ + 'DateAtStart', + 'NEWLINE' +]); -runLexerTests(test, tests); +test( + 'recognize a realistic transaction', + macro, + `1900/01/01 New York Steakhouse + Assets:Main Chequing -$23.05 = $100.00 + Expenses:Food\n`, + [ + 'DateAtStart', + 'Text', + 'NEWLINE', + 'INDENT', + { RealAccountName: ['Assets', 'Main Chequing'] }, + 'DASH', + { CommodityText: '$' }, + 'Number', + 'AMOUNT_WS', + 'EQUALS', + 'AMOUNT_WS', + { CommodityText: '$' }, + 'Number', + 'NEWLINE', + 'INDENT', + { RealAccountName: ['Expenses', 'Food'] }, + 'NEWLINE' + ] +); + +test( + 'recognize a maximal transaction', + macro, + `1900/01/01 * (a) payee|memo ; comment and tag: value, + Assets:Real -$1.00 @ 20 "green apples" = -$1.00 + (Assets:Virtual) 2CAD @@ 20$ == 0.1 + [Assets:VirtualBal:Sub] 1.3e4 (@@) 0.4 *== 1 ; comment tag1: val1, tag2: val2 + ; another comment with a tag:value\n`, + [ + 'DateAtStart', + 'TxnStatusIndicator', + { ParenValue: 'a' }, + 'Text', + 'PIPE', + 'Memo', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'InlineCommentTagComma', + 'NEWLINE', + 'INDENT', + { RealAccountName: ['Assets', 'Real'] }, + 'DASH', + { CommodityText: '$' }, + 'Number', + 'AMOUNT_WS', + 'AT', + 'AMOUNT_WS', + 'Number', + 'AMOUNT_WS', + { CommodityText: 'green apples' }, + 'AMOUNT_WS', + 'EQUALS', + 'AMOUNT_WS', + 'DASH', + { CommodityText: '$' }, + 'Number', + 'NEWLINE', + 'INDENT', + { VirtualAccountName: ['Assets', 'Virtual'] }, + 'Number', + { CommodityText: 'CAD' }, + 'AMOUNT_WS', + 'AT', + 'AT', + 'AMOUNT_WS', + 'Number', + { CommodityText: '$' }, + 'AMOUNT_WS', + 'EQUALS', + 'EQUALS', + 'AMOUNT_WS', + 'Number', + 'NEWLINE', + 'INDENT', + { VirtualBalancedAccountName: ['Assets', 'VirtualBal', 'Sub'] }, + 'Number', + 'AMOUNT_WS', + 'LPAREN', + 'AT', + 'AT', + 'RPAREN', + 'AMOUNT_WS', + 'Number', + 'AMOUNT_WS', + 'ASTERISK', + 'EQUALS', + 'EQUALS', + 'AMOUNT_WS', + 'Number', + 'AMOUNT_WS', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'InlineCommentTagComma', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'NEWLINE', + 'INDENT', + 'SemicolonComment', + 'InlineCommentText', + 'InlineCommentTagName', + 'InlineCommentTagColon', + 'InlineCommentTagValue', + 'NEWLINE' + ] +); + +test( + 'recognize a transaction with a payee note containing pipe characters', + macro, + '1900/01/01 New York Steakhouse|memo|note|something else|', + ['DateAtStart', 'Text', 'PIPE', 'Memo'] +); + +test( + 'does not recognize an invalid transaction date', + macro, + '2023-02-29 Invalid transaction', + [] +); // TODO: Add tests with more date patterns: https://hledger.org/1.30/hledger.html#smart-dates diff --git a/src/__tests__/lexer/utils.ts b/src/__tests__/lexer/utils.ts index b086264..72e10d9 100644 --- a/src/__tests__/lexer/utils.ts +++ b/src/__tests__/lexer/utils.ts @@ -1,4 +1,5 @@ -import test, { TestFn } from 'ava'; +// eslint-disable-next-line ava/no-ignored-test-files +import test from 'ava'; import HLedgerLexer from '../../lib/lexer'; import * as utils from '../utils'; @@ -13,19 +14,8 @@ function tokenize(pattern: string) { return utils.simplifyLexResult(HLedgerLexer.tokenize(pattern)); } -// TODO: Remove this function when all lexer tests are using the macro function. -export function runLexerTests(avaTest: TestFn, tests: LexerTest[]) { - for (const { pattern, expected, title } of tests) { - avaTest(title, (t) => { - const result = tokenize(pattern); - - t.deepEqual(result, expected, pattern); - }); - } -} - export const macro = test.macro<[string, unknown[]]>((t, pattern, expected) => { const result = tokenize(pattern); - t.deepEqual(result, expected, pattern); + t.deepEqual(result, expected, `${pattern}`); }); diff --git a/src/__tests__/raw_to_cooked_visitor/price_directive.test.ts b/src/__tests__/raw_to_cooked_visitor/price_directive.test.ts index 8caf5ea..24fba53 100644 --- a/src/__tests__/raw_to_cooked_visitor/price_directive.test.ts +++ b/src/__tests__/raw_to_cooked_visitor/price_directive.test.ts @@ -11,7 +11,7 @@ test('parses a price directive', (t) => { t.deepEqual( result.prices[0], { - date: { year: '1900', month: '01', day: '01', delimiter: '/' }, + date: { year: 1900, month: 1, day: 1 }, commodity: '$', price: { number: '10', diff --git a/src/__tests__/raw_to_cooked_visitor/transaction.test.ts b/src/__tests__/raw_to_cooked_visitor/transaction.test.ts index 914401c..cbf9e31 100644 --- a/src/__tests__/raw_to_cooked_visitor/transaction.test.ts +++ b/src/__tests__/raw_to_cooked_visitor/transaction.test.ts @@ -14,7 +14,7 @@ test('parses a transaction with a date', (t) => { ); t.deepEqual( result.transactions[0].date, - { year: '1900', month: '01', day: '02', delimiter: '/' }, + { year: 1900, month: 1, day: 2 }, 'should have parsed date' ); }); @@ -30,7 +30,7 @@ test('parses a transaction with a posting date', (t) => { ); t.deepEqual( result.transactions[0].postingDate, - { year: '2020', month: '01', day: '02', delimiter: '/' }, + { year: 2020, month: 1, day: 2 }, 'should have parsed posting date' ); });