From 524176faec2464223693f0870c52741369cd0fee Mon Sep 17 00:00:00 2001 From: Catherine Renelle <32781029+catrenelle@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:04:19 -0500 Subject: [PATCH] Fix .gitignore excluding frontend plugins and test files The broad plugins/ and imports/ patterns were matching directories at all levels, excluding frontend/src/plugins/vuetify.ts, frontend/src/views/imports/, frontend/src/views/plugins/, and src/Purrse.Tests/Plugins/. Changed to /plugins/ and /imports/ to only match root-level directories. This was the root cause of the Docker build failure - vuetify.ts was missing from the repo. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 6 +- frontend/src/plugins/vuetify.ts | 40 ++++++++++ frontend/src/views/imports/ImportsView.vue | 42 +++++++++++ frontend/src/views/plugins/PluginsView.vue | 6 ++ src/Purrse.Tests/Plugins/CSVParserTests.cs | 49 ++++++++++++ src/Purrse.Tests/Plugins/OFXParserTests.cs | 88 ++++++++++++++++++++++ src/Purrse.Tests/Plugins/QIFParserTests.cs | 30 ++++++++ 7 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 frontend/src/plugins/vuetify.ts create mode 100644 frontend/src/views/imports/ImportsView.vue create mode 100644 frontend/src/views/plugins/PluginsView.vue create mode 100644 src/Purrse.Tests/Plugins/CSVParserTests.cs create mode 100644 src/Purrse.Tests/Plugins/OFXParserTests.cs create mode 100644 src/Purrse.Tests/Plugins/QIFParserTests.cs diff --git a/.gitignore b/.gitignore index 9f84535..396f155 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,6 @@ docker-compose.override.yml # Claude Code .claude/ -# Plugins and imports (runtime data) -plugins/ -imports/ +# Plugins and imports (runtime data, root-level only) +/plugins/ +/imports/ diff --git a/frontend/src/plugins/vuetify.ts b/frontend/src/plugins/vuetify.ts new file mode 100644 index 0000000..3b850fc --- /dev/null +++ b/frontend/src/plugins/vuetify.ts @@ -0,0 +1,40 @@ +import 'vuetify/styles' +import { createVuetify } from 'vuetify' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' + +export default createVuetify({ + components, + directives, + theme: { + defaultTheme: 'dark', + themes: { + dark: { + dark: true, + colors: { + primary: '#7C4DFF', + secondary: '#FF6D00', + accent: '#00BFA5', + error: '#FF5252', + warning: '#FFC107', + info: '#2196F3', + success: '#4CAF50', + background: '#121212', + surface: '#1E1E1E', + } + }, + light: { + dark: false, + colors: { + primary: '#6200EA', + secondary: '#FF6D00', + accent: '#00BFA5', + error: '#FF5252', + warning: '#FFC107', + info: '#2196F3', + success: '#4CAF50', + } + } + } + } +}) diff --git a/frontend/src/views/imports/ImportsView.vue b/frontend/src/views/imports/ImportsView.vue new file mode 100644 index 0000000..77550c0 --- /dev/null +++ b/frontend/src/views/imports/ImportsView.vue @@ -0,0 +1,42 @@ + + + diff --git a/frontend/src/views/plugins/PluginsView.vue b/frontend/src/views/plugins/PluginsView.vue new file mode 100644 index 0000000..a264c5a --- /dev/null +++ b/frontend/src/views/plugins/PluginsView.vue @@ -0,0 +1,6 @@ + diff --git a/src/Purrse.Tests/Plugins/CSVParserTests.cs b/src/Purrse.Tests/Plugins/CSVParserTests.cs new file mode 100644 index 0000000..57f5801 --- /dev/null +++ b/src/Purrse.Tests/Plugins/CSVParserTests.cs @@ -0,0 +1,49 @@ +using FluentAssertions; +using Purrse.Plugins.CSV; + +namespace Purrse.Tests.Plugins; + +public class CSVParserTests +{ + private readonly CSVParserPlugin _parser = new(); + + [Fact] + public void CanParse_CSVFile_ReturnsTrue() + { + _parser.CanParse("transactions.csv", Stream.Null).Should().BeTrue(); + } + + [Fact] + public void CanParse_OFXFile_ReturnsFalse() + { + _parser.CanParse("statement.ofx", Stream.Null).Should().BeFalse(); + } + + [Fact] + public async Task ParseAsync_StandardCSV_ParsesTransactions() + { + var csv = "Date,Description,Amount\n01/15/2024,WALMART,-42.50\n01/20/2024,PAYCHECK,1500.00\n"; + + using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(csv)); + var result = await _parser.ParseAsync(stream, "test.csv"); + + result.Success.Should().BeTrue(); + result.Transactions.Should().HaveCount(2); + result.Transactions[0].PayeeName.Should().Be("WALMART"); + result.Transactions[0].Amount.Should().Be(-42.50m); + } + + [Fact] + public async Task ParseAsync_DebitCreditColumns_ParsesCorrectly() + { + var csv = "Date,Description,Debit,Credit\n01/15/2024,WALMART,42.50,\n01/20/2024,PAYCHECK,,1500.00\n"; + + using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(csv)); + var result = await _parser.ParseAsync(stream, "test.csv"); + + result.Success.Should().BeTrue(); + result.Transactions.Should().HaveCount(2); + result.Transactions[0].Amount.Should().Be(-42.50m); + result.Transactions[1].Amount.Should().Be(1500.00m); + } +} diff --git a/src/Purrse.Tests/Plugins/OFXParserTests.cs b/src/Purrse.Tests/Plugins/OFXParserTests.cs new file mode 100644 index 0000000..becf1f5 --- /dev/null +++ b/src/Purrse.Tests/Plugins/OFXParserTests.cs @@ -0,0 +1,88 @@ +using FluentAssertions; +using Purrse.Plugins.OFX; + +namespace Purrse.Tests.Plugins; + +public class OFXParserTests +{ + private readonly OFXParserPlugin _parser = new(); + + [Fact] + public void CanParse_OFXFile_ReturnsTrue() + { + _parser.CanParse("statement.ofx", Stream.Null).Should().BeTrue(); + } + + [Fact] + public void CanParse_QFXFile_ReturnsTrue() + { + _parser.CanParse("download.qfx", Stream.Null).Should().BeTrue(); + } + + [Fact] + public void CanParse_CSVFile_ReturnsFalse() + { + _parser.CanParse("data.csv", Stream.Null).Should().BeFalse(); + } + + [Fact] + public async Task ParseAsync_ValidXmlOFX_ParsesTransactions() + { + var ofxContent = @" + + + + + + 123456789 + + + 20240101 + 20240131 + + DEBIT + 20240115 + -42.50 + 20240115001 + WALMART STORE #1234 + Purchase + + + CREDIT + 20240120 + 1500.00 + 20240120001 + DIRECT DEPOSIT + + + + + +"; + + using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(ofxContent)); + var result = await _parser.ParseAsync(stream, "test.ofx"); + + result.Success.Should().BeTrue(); + result.Transactions.Should().HaveCount(2); + result.AccountIdentifier.Should().Be("123456789"); + + var debit = result.Transactions[0]; + debit.Amount.Should().Be(-42.50m); + debit.PayeeName.Should().Be("WALMART STORE #1234"); + debit.FitId.Should().Be("20240115001"); + debit.Date.Should().Be(new DateTime(2024, 1, 15)); + + var credit = result.Transactions[1]; + credit.Amount.Should().Be(1500.00m); + } + + [Fact] + public async Task ParseAsync_EmptyStream_ReturnsFailure() + { + using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("")); + var result = await _parser.ParseAsync(stream, "empty.ofx"); + + result.Success.Should().BeFalse(); + } +} diff --git a/src/Purrse.Tests/Plugins/QIFParserTests.cs b/src/Purrse.Tests/Plugins/QIFParserTests.cs new file mode 100644 index 0000000..bfa72c0 --- /dev/null +++ b/src/Purrse.Tests/Plugins/QIFParserTests.cs @@ -0,0 +1,30 @@ +using FluentAssertions; +using Purrse.Plugins.QIF; + +namespace Purrse.Tests.Plugins; + +public class QIFParserTests +{ + private readonly QIFParserPlugin _parser = new(); + + [Fact] + public void CanParse_QIFFile_ReturnsTrue() + { + _parser.CanParse("data.qif", Stream.Null).Should().BeTrue(); + } + + [Fact] + public async Task ParseAsync_ValidQIF_ParsesTransactions() + { + var qif = "!Type:Bank\nD01/15/2024\nT-42.50\nPWALMART\nMGroceries\n^\nD01/20/2024\nT1500.00\nPDIRECT DEPOSIT\n^\n"; + + using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(qif)); + var result = await _parser.ParseAsync(stream, "test.qif"); + + result.Success.Should().BeTrue(); + result.Transactions.Should().HaveCount(2); + result.Transactions[0].Amount.Should().Be(-42.50m); + result.Transactions[0].PayeeName.Should().Be("WALMART"); + result.Transactions[1].Amount.Should().Be(1500.00m); + } +}