diff --git a/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj b/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj index 85d73e01..27a01318 100644 --- a/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj +++ b/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ImportTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ImportTest.cs index f7f63a60..e5d9d58e 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ImportTest.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ImportTest.cs @@ -32,42 +32,22 @@ public async Task Import_ShouldImportTracks_WhenUsingSampleInputfile() await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Sample Artist 1 Clear"": - textbox: Sample Artist 1 - button ""Clear"" @@ -83,13 +63,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""00:05:00"": - textbox: 00:05:00 - cell - - row ""Increment Decrement Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell ""Sample Artist 2 Clear"": - textbox: Sample Artist 2 - button ""Clear"" @@ -106,13 +84,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:23 - cell: - button - - row ""Increment Decrement Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Sample Artist 3 Clear"": - textbox: Sample Artist 3 - button ""Clear"" @@ -129,13 +105,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:31 - cell: - button - - row ""Increment Decrement Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""Sample Artist 4 Clear"": - textbox: Sample Artist 4 - button ""Clear"" @@ -152,13 +126,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:19 - cell: - button - - row ""Increment Decrement Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Sample Artist 5 Clear"": - textbox: Sample Artist 5 - button ""Clear"" @@ -175,13 +147,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:41 - cell: - button - - row ""Increment Decrement Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Sample Artist 6 Clear"": - textbox: Sample Artist 6 - button ""Clear"" @@ -198,13 +168,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:07:00 - cell: - button - - row ""Increment Decrement Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Sample Artist 7 Clear"": - textbox: Sample Artist 7 - button ""Clear"" @@ -221,13 +189,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:14:00 - cell: - button - - row ""Increment Decrement Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Sample Artist 8 Clear"": - textbox: Sample Artist 8 - button ""Clear"" @@ -263,42 +229,22 @@ public async Task Import_ShouldBeEditable_WhenEditingTrack() await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Sample Artist 1 Clear"": - textbox: Sample Artist 1 - button ""Clear"" @@ -314,13 +260,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""00:05:00"": - textbox: 00:05:00 - cell - - row ""Increment Decrement Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell ""Sample Artist 2 Clear"": - textbox: Sample Artist 2 - button ""Clear"" @@ -337,13 +281,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:23 - cell: - button - - row ""Increment Decrement Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Sample Artist 3 Clear"": - textbox: Sample Artist 3 - button ""Clear"" @@ -360,13 +302,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:31 - cell: - button - - row ""Increment Decrement Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""Sample Artist 4 Clear"": - textbox: Sample Artist 4 - button ""Clear"" @@ -383,13 +323,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:19 - cell: - button - - row ""Increment Decrement Sample Artist 5 Clear Sample Title Edited 5 Clear 00:20:13 00:24:54 00:04:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Sample Artist 5 Clear Sample Title Edited 5 Clear 00:20:13 00:24:54 00:04:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Sample Artist 5 Clear"": - textbox: Sample Artist 5 - button ""Clear"" @@ -406,13 +344,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:41 - cell: - button - - row ""Increment Decrement Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Sample Artist 6 Clear"": - textbox: Sample Artist 6 - button ""Clear"" @@ -429,13 +365,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:07:00 - cell: - button - - row ""Increment Decrement Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Sample Artist 7 Clear"": - textbox: Sample Artist 7 - button ""Clear"" @@ -452,13 +386,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:14:00 - cell: - button - - row ""Increment Decrement Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Sample Artist 8 Clear"": - textbox: Sample Artist 8 - button ""Clear"" @@ -487,7 +419,761 @@ public async Task Import_ShouldImportTracks_WithSpecialTextfile() await importView.ImportFileAsync("Textimport-Bug-#54.txt"); await importView.SwitchImportProfileAsync("Textfile (just track data)"); await importView.Analyze(); - await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync("- table:\n - rowgroup:\n - row \"# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options\":\n - columnheader:\n - checkbox\n - columnheader \"# Sort Column options\":\n - button \"Sort\"\n - button \"Column options\"\n - columnheader \"Artist Sort Column options\":\n - button \"Sort\"\n - button \"Column options\"\n - columnheader \"Title Sort Column options\":\n - button \"Sort\"\n - button \"Column options\"\n - columnheader \"Begin Sort Column options\":\n - button \"Sort\"\n - button \"Column options\"\n - columnheader \"End Sort Column options\":\n - button \"Sort\"\n - button \"Column options\"\n - columnheader \"Length Sort Column options\":\n - button \"Sort\"\n - button \"Column options\"\n - rowgroup:\n - row /Increment Decrement Adriatique Clear X\\. Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"1\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Adriatique Clear\":\n - textbox: Adriatique\n - button \"Clear\"\n - button\n - cell \"X. Clear\":\n - textbox: X.\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Third Harmony Clear Fears And Dreams \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"2\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Third Harmony Clear\":\n - textbox: Third Harmony\n - button \"Clear\"\n - button\n - cell \"Fears And Dreams (Original Mix) Clear\":\n - textbox: Fears And Dreams (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Dele Sosimi Afrobeat Orchestra Clear Too Much Information \\(Laolu Remix; Edit\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"3\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Dele Sosimi Afrobeat Orchestra Clear\":\n - textbox: Dele Sosimi Afrobeat Orchestra\n - button \"Clear\"\n - button\n - cell \"Too Much Information (Laolu Remix; Edit) Clear\":\n - textbox: Too Much Information (Laolu Remix; Edit)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Edem, Govan Clear Ankh \\(Onetwo MX Remix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"4\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Edem, Govan Clear\":\n - textbox: Edem, Govan\n - button \"Clear\"\n - button\n - cell \"Ankh (Onetwo MX Remix) Clear\":\n - textbox: Ankh (Onetwo MX Remix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Jody Wisternoff Clear For All Time \\(feat\\. Hendrik Burkhard\\) \\(Extended Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"5\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Jody Wisternoff Clear\":\n - textbox: Jody Wisternoff\n - button \"Clear\"\n - button\n - cell \"For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear\":\n - textbox: For All Time (feat. Hendrik Burkhard) (Extended Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Einmusik Clear Bead \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"6\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Einmusik Clear\":\n - textbox: Einmusik\n - button \"Clear\"\n - button\n - cell \"Bead (Original Mix) Clear\":\n - textbox: Bead (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Sebastien Leger Clear La Danse du Scorpion Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"7\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Sebastien Leger Clear\":\n - textbox: Sebastien Leger\n - button \"Clear\"\n - button\n - cell \"La Danse du Scorpion Clear\":\n - textbox: La Danse du Scorpion\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Paul Thomas & Solid Stone Clear La Bombo \\(Solid Stone Remix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"8\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Paul Thomas & Solid Stone Clear\":\n - textbox: Paul Thomas & Solid Stone\n - button \"Clear\"\n - button\n - cell \"La Bombo (Solid Stone Remix) Clear\":\n - textbox: La Bombo (Solid Stone Remix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement GusGus Clear Crossfade \\(Maceo Plex Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: \"9\"\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"GusGus Clear\":\n - textbox: GusGus\n - button \"Clear\"\n - button\n - cell \"Crossfade (Maceo Plex Mix) Clear\":\n - textbox: Crossfade (Maceo Plex Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Klangkarussell Clear Time \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Klangkarussell Clear\":\n - textbox: Klangkarussell\n - button \"Clear\"\n - button\n - cell \"Time (Original Mix) Clear\":\n - textbox: Time (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Anysense & Un:said Clear Missing Path \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Anysense & Un:said Clear\":\n - textbox: Anysense & Un:said\n - button \"Clear\"\n - button\n - cell \"Missing Path (Original Mix) Clear\":\n - textbox: Missing Path (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Space Food Clear Bombay Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Space Food Clear\":\n - textbox: Space Food\n - button \"Clear\"\n - button\n - cell \"Bombay Clear\":\n - textbox: Bombay\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement SHDW & Obscure Shape Clear Wächter der Nacht \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"SHDW & Obscure Shape Clear\":\n - textbox: SHDW & Obscure Shape\n - button \"Clear\"\n - button\n - cell \"Wächter der Nacht (Original Mix) Clear\":\n - textbox: Wächter der Nacht (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement HOSH Clear Karma Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"HOSH Clear\":\n - textbox: HOSH\n - button \"Clear\"\n - button\n - cell \"Karma Clear\":\n - textbox: Karma\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Alexey Union Clear Olympia \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Alexey Union Clear\":\n - textbox: Alexey Union\n - button \"Clear\"\n - button\n - cell \"Olympia (Original Mix) Clear\":\n - textbox: Olympia (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Paul Taylor Clear Afterglow Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Paul Taylor Clear\":\n - textbox: Paul Taylor\n - button \"Clear\"\n - button\n - cell \"Afterglow Clear\":\n - textbox: Afterglow\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Philter Clear Stranger Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Philter Clear\":\n - textbox: Philter\n - button \"Clear\"\n - button\n - cell \"Stranger Clear\":\n - textbox: Stranger\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Skizologic Clear Hypersphere \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Skizologic Clear\":\n - textbox: Skizologic\n - button \"Clear\"\n - button\n - cell \"Hypersphere (Original Mix) Clear\":\n - textbox: Hypersphere (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Thomas Schumacher, Caitlin Clear All of You \\(Remix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Thomas Schumacher, Caitlin Clear\":\n - textbox: Thomas Schumacher, Caitlin\n - button \"Clear\"\n - button\n - cell \"All of You (Remix) Clear\":\n - textbox: All of You (Remix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement A\\. Skomoroh Clear White Horse Conquest \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"A. Skomoroh Clear\":\n - textbox: A. Skomoroh\n - button \"Clear\"\n - button\n - cell \"White Horse Conquest (Original Mix) Clear\":\n - textbox: White Horse Conquest (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Patrik Berg Clear Bright \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Patrik Berg Clear\":\n - textbox: Patrik Berg\n - button \"Clear\"\n - button\n - cell \"Bright (Original Mix) Clear\":\n - textbox: Bright (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Hidden Empire Clear Bengal Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Hidden Empire Clear\":\n - textbox: Hidden Empire\n - button \"Clear\"\n - button\n - cell \"Bengal Clear\":\n - textbox: Bengal\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Mario Ochoa Clear Levitate Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Mario Ochoa Clear\":\n - textbox: Mario Ochoa\n - button \"Clear\"\n - button\n - cell \"Levitate Clear\":\n - textbox: Levitate\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Raul Facio Clear Eyes Wide Shut \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Raul Facio Clear\":\n - textbox: Raul Facio\n - button \"Clear\"\n - button\n - cell \"Eyes Wide Shut (Original Mix) Clear\":\n - textbox: Eyes Wide Shut (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Soolver Clear Regular \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Soolver Clear\":\n - textbox: Soolver\n - button \"Clear\"\n - button\n - cell \"Regular (Original Mix) Clear\":\n - textbox: Regular (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Weska Clear EQ64 \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Weska Clear\":\n - textbox: Weska\n - button \"Clear\"\n - button\n - cell \"EQ64 (Original Mix) Clear\":\n - textbox: EQ64 (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Tempo Giusto Clear The Fall \\(Extended Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Tempo Giusto Clear\":\n - textbox: Tempo Giusto\n - button \"Clear\"\n - button\n - cell \"The Fall (Extended Mix) Clear\":\n - textbox: The Fall (Extended Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Vlind & Asteroid & Gary Leroy Clear Trinity \\(Extended Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Vlind & Asteroid & Gary Leroy Clear\":\n - textbox: Vlind & Asteroid & Gary Leroy\n - button \"Clear\"\n - button\n - cell \"Trinity (Extended Mix) Clear\":\n - textbox: Trinity (Extended Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Astral Legacy Clear Vaveyla \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Astral Legacy Clear\":\n - textbox: Astral Legacy\n - button \"Clear\"\n - button\n - cell \"Vaveyla (Original Mix) Clear\":\n - textbox: Vaveyla (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Gerrox Clear Chakra \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Gerrox Clear\":\n - textbox: Gerrox\n - button \"Clear\"\n - button\n - cell \"Chakra (Original Mix) Clear\":\n - textbox: Chakra (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Charlotte De Witte Clear Pattern Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Charlotte De Witte Clear\":\n - textbox: Charlotte De Witte\n - button \"Clear\"\n - button\n - cell \"Pattern Clear\":\n - textbox: Pattern\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Space Food Clear Amabey Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Space Food Clear\":\n - textbox: Space Food\n - button \"Clear\"\n - button\n - cell \"Amabey Clear\":\n - textbox: Amabey\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement ARTBAT Clear Papilion \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"ARTBAT Clear\":\n - textbox: ARTBAT\n - button \"Clear\"\n - button\n - cell \"Papilion (Original Mix) Clear\":\n - textbox: Papilion (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement PETER PAHN Clear Enjoy Infinity \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"PETER PAHN Clear\":\n - textbox: PETER PAHN\n - button \"Clear\"\n - button\n - cell \"Enjoy Infinity (Original Mix) Clear\":\n - textbox: Enjoy Infinity (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Solitek Clear Instinct \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Solitek Clear\":\n - textbox: Solitek\n - button \"Clear\"\n - button\n - cell \"Instinct (Original Mix) Clear\":\n - textbox: Instinct (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Veerus Clear Heavy Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Veerus Clear\":\n - textbox: Veerus\n - button \"Clear\"\n - button\n - cell \"Heavy Clear\":\n - textbox: Heavy\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Secret Cinema & Reinier Zonneveld Clear Pain Thing \\(Original Mix\\) Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Secret Cinema & Reinier Zonneveld Clear\":\n - textbox: Secret Cinema & Reinier Zonneveld\n - button \"Clear\"\n - button\n - cell \"Pain Thing (Original Mix) Clear\":\n - textbox: Pain Thing (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Amelie Lens Clear Hypnotized Clear \\d+:\\d+:\\d+ \\d+:\\d+:\\d+ \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Amelie Lens Clear\":\n - textbox: Amelie Lens\n - button \"Clear\"\n - button\n - cell \"Hypnotized Clear\":\n - textbox: Hypnotized\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - row /Increment Decrement Nikolay Kirov Clear Chasing the Sun \\(Original Mix\\) Clear \\d+:\\d+:\\d+/:\n - cell:\n - checkbox\n - cell \"Increment Decrement\":\n - spinbutton: /\\d+/\n - button \"Increment\"\n - button \"Decrement\"\n - cell \"Nikolay Kirov Clear\":\n - textbox: Nikolay Kirov\n - button \"Clear\"\n - button\n - cell \"Chasing the Sun (Original Mix) Clear\":\n - textbox: Chasing the Sun (Original Mix)\n - button \"Clear\"\n - button\n - cell /\\d+:\\d+:\\d+/:\n - textbox: /\\d+:\\d+:\\d+/\n - cell:\n - textbox\n - cell:\n - textbox\n - rowgroup:\n - row"); + await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup: + - 'row ""Select all rows # Artist Title Begin End Length""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" + - rowgroup: + - row ""Select row 1 Adriatique Clear X. Clear 00:00:00 00:05:24 00:05:24"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" + - cell ""Adriatique Clear"": + - textbox: Adriatique + - button ""Clear"" + - button + - cell ""X. Clear"": + - textbox: X. + - button ""Clear"" + - button + - cell ""00:00:00"": + - textbox: 00:00:00 + - cell ""00:05:24"": + - textbox: 00:05:24 + - cell ""00:05:24"": + - textbox: 00:05:24 + - row ""Select row 2 Third Harmony Clear Fears And Dreams (Original Mix) Clear 00:05:24 00:10:39 00:05:15"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" + - cell ""Third Harmony Clear"": + - textbox: Third Harmony + - button ""Clear"" + - button + - cell ""Fears And Dreams (Original Mix) Clear"": + - textbox: Fears And Dreams (Original Mix) + - button ""Clear"" + - button + - cell ""00:05:24"": + - textbox: 00:05:24 + - cell ""00:10:39"": + - textbox: 00:10:39 + - cell ""00:05:15"": + - textbox: 00:05:15 + - row ""Select row 3 Dele Sosimi Afrobeat Orchestra Clear Too Much Information (Laolu Remix; Edit) Clear 00:10:39 00:17:06 00:06:27"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" + - cell ""Dele Sosimi Afrobeat Orchestra Clear"": + - textbox: Dele Sosimi Afrobeat Orchestra + - button ""Clear"" + - button + - cell ""Too Much Information (Laolu Remix; Edit) Clear"": + - textbox: Too Much Information (Laolu Remix; Edit) + - button ""Clear"" + - button + - cell ""00:10:39"": + - textbox: 00:10:39 + - cell ""00:17:06"": + - textbox: 00:17:06 + - cell ""00:06:27"": + - textbox: 00:06:27 + - row ""Select row 4 Edem, Govan Clear Ankh (Onetwo MX Remix) Clear 00:17:06 00:23:21 00:06:15"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" + - cell ""Edem, Govan Clear"": + - textbox: Edem, Govan + - button ""Clear"" + - button + - cell ""Ankh (Onetwo MX Remix) Clear"": + - textbox: Ankh (Onetwo MX Remix) + - button ""Clear"" + - button + - cell ""00:17:06"": + - textbox: 00:17:06 + - cell ""00:23:21"": + - textbox: 00:23:21 + - cell ""00:06:15"": + - textbox: 00:06:15 + - row ""Select row 5 Jody Wisternoff Clear For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear 00:23:21 00:29:02 00:05:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" + - cell ""Jody Wisternoff Clear"": + - textbox: Jody Wisternoff + - button ""Clear"" + - button + - cell ""For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear"": + - textbox: For All Time (feat. Hendrik Burkhard) (Extended Mix) + - button ""Clear"" + - button + - cell ""00:23:21"": + - textbox: 00:23:21 + - cell ""00:29:02"": + - textbox: 00:29:02 + - cell ""00:05:41"": + - textbox: 00:05:41 + - row ""Select row 6 Einmusik Clear Bead (Original Mix) Clear 00:29:02 00:34:27 00:05:25"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" + - cell ""Einmusik Clear"": + - textbox: Einmusik + - button ""Clear"" + - button + - cell ""Bead (Original Mix) Clear"": + - textbox: Bead (Original Mix) + - button ""Clear"" + - button + - cell ""00:29:02"": + - textbox: 00:29:02 + - cell ""00:34:27"": + - textbox: 00:34:27 + - cell ""00:05:25"": + - textbox: 00:05:25 + - row ""Select row 7 Sebastien Leger Clear La Danse du Scorpion Clear 00:34:27 00:40:59 00:06:32"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" + - cell ""Sebastien Leger Clear"": + - textbox: Sebastien Leger + - button ""Clear"" + - button + - cell ""La Danse du Scorpion Clear"": + - textbox: La Danse du Scorpion + - button ""Clear"" + - button + - cell ""00:34:27"": + - textbox: 00:34:27 + - cell ""00:40:59"": + - textbox: 00:40:59 + - cell ""00:06:32"": + - textbox: 00:06:32 + - row ""Select row 8 Paul Thomas & Solid Stone Clear La Bombo (Solid Stone Remix) Clear 00:40:59 00:46:19 00:05:20"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" + - cell ""Paul Thomas & Solid Stone Clear"": + - textbox: Paul Thomas & Solid Stone + - button ""Clear"" + - button + - cell ""La Bombo (Solid Stone Remix) Clear"": + - textbox: La Bombo (Solid Stone Remix) + - button ""Clear"" + - button + - cell ""00:40:59"": + - textbox: 00:40:59 + - cell ""00:46:19"": + - textbox: 00:46:19 + - cell ""00:05:20"": + - textbox: 00:05:20 + - row ""Select row 9 GusGus Clear Crossfade (Maceo Plex Mix) Clear 00:46:19 00:52:20 00:06:01"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""9"" + - cell ""GusGus Clear"": + - textbox: GusGus + - button ""Clear"" + - button + - cell ""Crossfade (Maceo Plex Mix) Clear"": + - textbox: Crossfade (Maceo Plex Mix) + - button ""Clear"" + - button + - cell ""00:46:19"": + - textbox: 00:46:19 + - cell ""00:52:20"": + - textbox: 00:52:20 + - cell ""00:06:01"": + - textbox: 00:06:01 + - row ""Select row 10 Klangkarussell Clear Time (Original Mix) Clear 00:52:20 00:56:19 00:03:59"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""10"" + - cell ""Klangkarussell Clear"": + - textbox: Klangkarussell + - button ""Clear"" + - button + - cell ""Time (Original Mix) Clear"": + - textbox: Time (Original Mix) + - button ""Clear"" + - button + - cell ""00:52:20"": + - textbox: 00:52:20 + - cell ""00:56:19"": + - textbox: 00:56:19 + - cell ""00:03:59"": + - textbox: 00:03:59 + - row ""Select row 11 Anysense & Un:said Clear Missing Path (Original Mix) Clear 00:56:19 01:01:41 00:05:22"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""11"" + - cell ""Anysense & Un:said Clear"": + - textbox: Anysense & Un:said + - button ""Clear"" + - button + - cell ""Missing Path (Original Mix) Clear"": + - textbox: Missing Path (Original Mix) + - button ""Clear"" + - button + - cell ""00:56:19"": + - textbox: 00:56:19 + - cell ""01:01:41"": + - textbox: 01:01:41 + - cell ""00:05:22"": + - textbox: 00:05:22 + - row ""Select row 12 Space Food Clear Bombay Clear 01:01:41 01:06:33 00:04:52"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""12"" + - cell ""Space Food Clear"": + - textbox: Space Food + - button ""Clear"" + - button + - cell ""Bombay Clear"": + - textbox: Bombay + - button ""Clear"" + - button + - cell ""01:01:41"": + - textbox: 01:01:41 + - cell ""01:06:33"": + - textbox: 01:06:33 + - cell ""00:04:52"": + - textbox: 00:04:52 + - row ""Select row 13 SHDW & Obscure Shape Clear Wächter der Nacht (Original Mix) Clear 01:06:33 01:11:04 00:04:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""13"" + - cell ""SHDW & Obscure Shape Clear"": + - textbox: SHDW & Obscure Shape + - button ""Clear"" + - button + - cell ""Wächter der Nacht (Original Mix) Clear"": + - textbox: Wächter der Nacht (Original Mix) + - button ""Clear"" + - button + - cell ""01:06:33"": + - textbox: 01:06:33 + - cell ""01:11:04"": + - textbox: 01:11:04 + - cell ""00:04:31"": + - textbox: 00:04:31 + - row ""Select row 14 HOSH Clear Karma Clear 01:11:04 01:15:28 00:04:24"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""14"" + - cell ""HOSH Clear"": + - textbox: HOSH + - button ""Clear"" + - button + - cell ""Karma Clear"": + - textbox: Karma + - button ""Clear"" + - button + - cell ""01:11:04"": + - textbox: 01:11:04 + - cell ""01:15:28"": + - textbox: 01:15:28 + - cell ""00:04:24"": + - textbox: 00:04:24 + - row ""Select row 15 Alexey Union Clear Olympia (Original Mix) Clear 01:15:28 01:21:08 00:05:40"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""15"" + - cell ""Alexey Union Clear"": + - textbox: Alexey Union + - button ""Clear"" + - button + - cell ""Olympia (Original Mix) Clear"": + - textbox: Olympia (Original Mix) + - button ""Clear"" + - button + - cell ""01:15:28"": + - textbox: 01:15:28 + - cell ""01:21:08"": + - textbox: 01:21:08 + - cell ""00:05:40"": + - textbox: 00:05:40 + - row ""Select row 16 Paul Taylor Clear Afterglow Clear 01:21:08 01:25:38 00:04:30"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""16"" + - cell ""Paul Taylor Clear"": + - textbox: Paul Taylor + - button ""Clear"" + - button + - cell ""Afterglow Clear"": + - textbox: Afterglow + - button ""Clear"" + - button + - cell ""01:21:08"": + - textbox: 01:21:08 + - cell ""01:25:38"": + - textbox: 01:25:38 + - cell ""00:04:30"": + - textbox: 00:04:30 + - row ""Select row 17 Philter Clear Stranger Clear 01:25:38 01:31:52 00:06:14"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""17"" + - cell ""Philter Clear"": + - textbox: Philter + - button ""Clear"" + - button + - cell ""Stranger Clear"": + - textbox: Stranger + - button ""Clear"" + - button + - cell ""01:25:38"": + - textbox: 01:25:38 + - cell ""01:31:52"": + - textbox: 01:31:52 + - cell ""00:06:14"": + - textbox: 00:06:14 + - row ""Select row 18 Skizologic Clear Hypersphere (Original Mix) Clear 01:31:52 01:36:40 00:04:48"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""18"" + - cell ""Skizologic Clear"": + - textbox: Skizologic + - button ""Clear"" + - button + - cell ""Hypersphere (Original Mix) Clear"": + - textbox: Hypersphere (Original Mix) + - button ""Clear"" + - button + - cell ""01:31:52"": + - textbox: 01:31:52 + - cell ""01:36:40"": + - textbox: 01:36:40 + - cell ""00:04:48"": + - textbox: 00:04:48 + - row ""Select row 19 Thomas Schumacher, Caitlin Clear All of You (Remix) Clear 01:36:40 01:42:16 00:05:36"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""19"" + - cell ""Thomas Schumacher, Caitlin Clear"": + - textbox: Thomas Schumacher, Caitlin + - button ""Clear"" + - button + - cell ""All of You (Remix) Clear"": + - textbox: All of You (Remix) + - button ""Clear"" + - button + - cell ""01:36:40"": + - textbox: 01:36:40 + - cell ""01:42:16"": + - textbox: 01:42:16 + - cell ""00:05:36"": + - textbox: 00:05:36 + - row ""Select row 20 A. Skomoroh Clear White Horse Conquest (Original Mix) Clear 01:42:16 01:47:04 00:04:48"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""20"" + - cell ""A. Skomoroh Clear"": + - textbox: A. Skomoroh + - button ""Clear"" + - button + - cell ""White Horse Conquest (Original Mix) Clear"": + - textbox: White Horse Conquest (Original Mix) + - button ""Clear"" + - button + - cell ""01:42:16"": + - textbox: 01:42:16 + - cell ""01:47:04"": + - textbox: 01:47:04 + - cell ""00:04:48"": + - textbox: 00:04:48 + - row ""Select row 21 Patrik Berg Clear Bright (Original Mix) Clear 01:47:04 01:52:37 00:05:33"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""21"" + - cell ""Patrik Berg Clear"": + - textbox: Patrik Berg + - button ""Clear"" + - button + - cell ""Bright (Original Mix) Clear"": + - textbox: Bright (Original Mix) + - button ""Clear"" + - button + - cell ""01:47:04"": + - textbox: 01:47:04 + - cell ""01:52:37"": + - textbox: 01:52:37 + - cell ""00:05:33"": + - textbox: 00:05:33 + - row ""Select row 22 Hidden Empire Clear Bengal Clear 01:52:37 01:58:05 00:05:28"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""22"" + - cell ""Hidden Empire Clear"": + - textbox: Hidden Empire + - button ""Clear"" + - button + - cell ""Bengal Clear"": + - textbox: Bengal + - button ""Clear"" + - button + - cell ""01:52:37"": + - textbox: 01:52:37 + - cell ""01:58:05"": + - textbox: 01:58:05 + - cell ""00:05:28"": + - textbox: 00:05:28 + - row ""Select row 23 Mario Ochoa Clear Levitate Clear 01:58:05 02:03:00 00:04:55"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""23"" + - cell ""Mario Ochoa Clear"": + - textbox: Mario Ochoa + - button ""Clear"" + - button + - cell ""Levitate Clear"": + - textbox: Levitate + - button ""Clear"" + - button + - cell ""01:58:05"": + - textbox: 01:58:05 + - cell ""02:03:00"": + - textbox: 02:03:00 + - cell ""00:04:55"": + - textbox: 00:04:55 + - row ""Select row 24 Raul Facio Clear Eyes Wide Shut (Original Mix) Clear 02:03:00 02:08:21 00:05:21"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""24"" + - cell ""Raul Facio Clear"": + - textbox: Raul Facio + - button ""Clear"" + - button + - cell ""Eyes Wide Shut (Original Mix) Clear"": + - textbox: Eyes Wide Shut (Original Mix) + - button ""Clear"" + - button + - cell ""02:03:00"": + - textbox: 02:03:00 + - cell ""02:08:21"": + - textbox: 02:08:21 + - cell ""00:05:21"": + - textbox: 00:05:21 + - row ""Select row 25 Soolver Clear Regular (Original Mix) Clear 02:08:21 02:14:31 00:06:10"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""25"" + - cell ""Soolver Clear"": + - textbox: Soolver + - button ""Clear"" + - button + - cell ""Regular (Original Mix) Clear"": + - textbox: Regular (Original Mix) + - button ""Clear"" + - button + - cell ""02:08:21"": + - textbox: 02:08:21 + - cell ""02:14:31"": + - textbox: 02:14:31 + - cell ""00:06:10"": + - textbox: 00:06:10 + - row ""Select row 26 Weska Clear EQ64 (Original Mix) Clear 02:14:31 02:18:35 00:04:04"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""26"" + - cell ""Weska Clear"": + - textbox: Weska + - button ""Clear"" + - button + - cell ""EQ64 (Original Mix) Clear"": + - textbox: EQ64 (Original Mix) + - button ""Clear"" + - button + - cell ""02:14:31"": + - textbox: 02:14:31 + - cell ""02:18:35"": + - textbox: 02:18:35 + - cell ""00:04:04"": + - textbox: 00:04:04 + - row ""Select row 27 Tempo Giusto Clear The Fall (Extended Mix) Clear 02:18:35 02:24:12 00:05:37"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""27"" + - cell ""Tempo Giusto Clear"": + - textbox: Tempo Giusto + - button ""Clear"" + - button + - cell ""The Fall (Extended Mix) Clear"": + - textbox: The Fall (Extended Mix) + - button ""Clear"" + - button + - cell ""02:18:35"": + - textbox: 02:18:35 + - cell ""02:24:12"": + - textbox: 02:24:12 + - cell ""00:05:37"": + - textbox: 00:05:37 + - row ""Select row 28 Vlind & Asteroid & Gary Leroy Clear Trinity (Extended Mix) Clear 02:24:12 02:29:38 00:05:26"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""28"" + - cell ""Vlind & Asteroid & Gary Leroy Clear"": + - textbox: Vlind & Asteroid & Gary Leroy + - button ""Clear"" + - button + - cell ""Trinity (Extended Mix) Clear"": + - textbox: Trinity (Extended Mix) + - button ""Clear"" + - button + - cell ""02:24:12"": + - textbox: 02:24:12 + - cell ""02:29:38"": + - textbox: 02:29:38 + - cell ""00:05:26"": + - textbox: 00:05:26 + - row ""Select row 29 Astral Legacy Clear Vaveyla (Original Mix) Clear 02:29:38 02:32:52 00:03:14"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""29"" + - cell ""Astral Legacy Clear"": + - textbox: Astral Legacy + - button ""Clear"" + - button + - cell ""Vaveyla (Original Mix) Clear"": + - textbox: Vaveyla (Original Mix) + - button ""Clear"" + - button + - cell ""02:29:38"": + - textbox: 02:29:38 + - cell ""02:32:52"": + - textbox: 02:32:52 + - cell ""00:03:14"": + - textbox: 00:03:14 + - row ""Select row 30 Gerrox Clear Chakra (Original Mix) Clear 02:32:52 02:37:00 00:04:08"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""30"" + - cell ""Gerrox Clear"": + - textbox: Gerrox + - button ""Clear"" + - button + - cell ""Chakra (Original Mix) Clear"": + - textbox: Chakra (Original Mix) + - button ""Clear"" + - button + - cell ""02:32:52"": + - textbox: 02:32:52 + - cell ""02:37:00"": + - textbox: 02:37:00 + - cell ""00:04:08"": + - textbox: 00:04:08 + - row ""Select row 31 Charlotte De Witte Clear Pattern Clear 02:37:00 02:41:55 00:04:55"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""31"" + - cell ""Charlotte De Witte Clear"": + - textbox: Charlotte De Witte + - button ""Clear"" + - button + - cell ""Pattern Clear"": + - textbox: Pattern + - button ""Clear"" + - button + - cell ""02:37:00"": + - textbox: 02:37:00 + - cell ""02:41:55"": + - textbox: 02:41:55 + - cell ""00:04:55"": + - textbox: 00:04:55 + - row ""Select row 32 Space Food Clear Amabey Clear 02:41:55 02:46:55 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""32"" + - cell ""Space Food Clear"": + - textbox: Space Food + - button ""Clear"" + - button + - cell ""Amabey Clear"": + - textbox: Amabey + - button ""Clear"" + - button + - cell ""02:41:55"": + - textbox: 02:41:55 + - cell ""02:46:55"": + - textbox: 02:46:55 + - cell ""00:05:00"": + - textbox: 00:05:00 + - row ""Select row 33 ARTBAT Clear Papilion (Original Mix) Clear 02:46:55 02:51:13 00:04:18"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""33"" + - cell ""ARTBAT Clear"": + - textbox: ARTBAT + - button ""Clear"" + - button + - cell ""Papilion (Original Mix) Clear"": + - textbox: Papilion (Original Mix) + - button ""Clear"" + - button + - cell ""02:46:55"": + - textbox: 02:46:55 + - cell ""02:51:13"": + - textbox: 02:51:13 + - cell ""00:04:18"": + - textbox: 00:04:18 + - row ""Select row 34 PETER PAHN Clear Enjoy Infinity (Original Mix) Clear 02:51:13 02:56:08 00:04:55"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""34"" + - cell ""PETER PAHN Clear"": + - textbox: PETER PAHN + - button ""Clear"" + - button + - cell ""Enjoy Infinity (Original Mix) Clear"": + - textbox: Enjoy Infinity (Original Mix) + - button ""Clear"" + - button + - cell ""02:51:13"": + - textbox: 02:51:13 + - cell ""02:56:08"": + - textbox: 02:56:08 + - cell ""00:04:55"": + - textbox: 00:04:55 + - row ""Select row 35 Solitek Clear Instinct (Original Mix) Clear 02:56:08 03:00:57 00:04:49"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""35"" + - cell ""Solitek Clear"": + - textbox: Solitek + - button ""Clear"" + - button + - cell ""Instinct (Original Mix) Clear"": + - textbox: Instinct (Original Mix) + - button ""Clear"" + - button + - cell ""02:56:08"": + - textbox: 02:56:08 + - cell ""03:00:57"": + - textbox: 03:00:57 + - cell ""00:04:49"": + - textbox: 00:04:49 + - row ""Select row 36 Veerus Clear Heavy Clear 03:00:57 03:05:19 00:04:22"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""36"" + - cell ""Veerus Clear"": + - textbox: Veerus + - button ""Clear"" + - button + - cell ""Heavy Clear"": + - textbox: Heavy + - button ""Clear"" + - button + - cell ""03:00:57"": + - textbox: 03:00:57 + - cell ""03:05:19"": + - textbox: 03:05:19 + - cell ""00:04:22"": + - textbox: 00:04:22 + - row ""Select row 37 Secret Cinema & Reinier Zonneveld Clear Pain Thing (Original Mix) Clear 03:05:19 03:09:38 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""37"" + - cell ""Secret Cinema & Reinier Zonneveld Clear"": + - textbox: Secret Cinema & Reinier Zonneveld + - button ""Clear"" + - button + - cell ""Pain Thing (Original Mix) Clear"": + - textbox: Pain Thing (Original Mix) + - button ""Clear"" + - button + - cell ""03:05:19"": + - textbox: 03:05:19 + - cell ""03:09:38"": + - textbox: 03:09:38 + - cell ""00:04:19"": + - textbox: 00:04:19 + - row ""Select row 38 Amelie Lens Clear Hypnotized Clear 03:09:38 03:13:13 00:03:35"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""38"" + - cell ""Amelie Lens Clear"": + - textbox: Amelie Lens + - button ""Clear"" + - button + - cell ""Hypnotized Clear"": + - textbox: Hypnotized + - button ""Clear"" + - button + - cell ""03:09:38"": + - textbox: 03:09:38 + - cell ""03:13:13"": + - textbox: 03:13:13 + - cell ""00:03:35"": + - textbox: 00:03:35 + - row ""Select row 39 Nikolay Kirov Clear Chasing the Sun (Original Mix) Clear 03:13:13"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""39"" + - cell ""Nikolay Kirov Clear"": + - textbox: Nikolay Kirov + - button ""Clear"" + - button + - cell ""Chasing the Sun (Original Mix) Clear"": + - textbox: Chasing the Sun (Original Mix) + - button ""Clear"" + - button + - cell ""03:13:13"": + - textbox: 03:13:13 + - cell: + - textbox + - cell: + - textbox + - rowgroup: + - row"); } [TestMethod] @@ -502,41 +1188,21 @@ public async Task Import_ShouldImportTracks_WhenUsingSampleInputfile2() await Expect(importView.CuesheetTitleInput).ToBeEmptyAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - rowgroup: - - row ""Increment Decrement Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Sample Artist 1 Clear"": - textbox: Sample Artist 1 - button ""Clear"" @@ -551,13 +1217,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:00 - cell ""00:05:00"": - textbox: 00:05:00 - - row ""Increment Decrement Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell ""Sample Artist 2 Clear"": - textbox: Sample Artist 2 - button ""Clear"" @@ -572,13 +1236,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:09:23 - cell ""00:04:23"": - textbox: 00:04:23 - - row ""Increment Decrement Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Sample Artist 3 Clear"": - textbox: Sample Artist 3 - button ""Clear"" @@ -593,13 +1255,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:15:54 - cell ""00:06:31"": - textbox: 00:06:31 - - row ""Increment Decrement Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""Sample Artist 4 Clear"": - textbox: Sample Artist 4 - button ""Clear"" @@ -614,13 +1274,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:20:13 - cell ""00:04:19"": - textbox: 00:04:19 - - row ""Increment Decrement Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Sample Artist 5 Clear"": - textbox: Sample Artist 5 - button ""Clear"" @@ -635,13 +1293,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:24:54 - cell ""00:04:41"": - textbox: 00:04:41 - - row ""Increment Decrement Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Sample Artist 6 Clear"": - textbox: Sample Artist 6 - button ""Clear"" @@ -656,13 +1312,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:31:54 - cell ""00:07:00"": - textbox: 00:07:00 - - row ""Increment Decrement Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Sample Artist 7 Clear"": - textbox: Sample Artist 7 - button ""Clear"" @@ -677,13 +1331,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:45:54 - cell ""00:14:00"": - textbox: 00:14:00 - - row ""Increment Decrement Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Sample Artist 8 Clear"": - textbox: Sample Artist 8 - button ""Clear"" @@ -713,42 +1365,22 @@ public async Task Import_ShouldImportTracks_WithTraktorOutput() await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Nachap Clear Glass Clear 00:00:00 00:11:56 00:11:56"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Nachap Clear Glass Clear 00:00:00 00:11:56 00:11:56"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Nachap Clear"": - textbox: Nachap - button ""Clear"" @@ -764,13 +1396,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""00:11:56"": - textbox: 00:11:56 - cell - - row ""Increment Decrement Progressive + Melodic DECK Clear 00:11:56 00:13:31 00:01:35"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Progressive + Melodic DECK Clear 00:11:56 00:13:31 00:01:35"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell: - textbox - button @@ -786,13 +1416,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:01:35 - cell: - button - - row ""Increment Decrement Nomer 21 Clear Depersonalization Clear 00:13:31 00:19:14 00:05:43"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Nomer 21 Clear Depersonalization Clear 00:13:31 00:19:14 00:05:43"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Nomer 21 Clear"": - textbox: Nomer 21 - button ""Clear"" @@ -809,13 +1437,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:43 - cell: - button - - row ""Increment Decrement SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Showing Off Clear 00:19:14 00:24:02 00:04:48"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Showing Off Clear 00:19:14 00:24:02 00:04:48"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear"": - textbox: SevenEver, Nopopstar, 2JOHN'S & Eugene Jay - button ""Clear"" @@ -832,13 +1458,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:48 - cell: - button - - row ""Increment Decrement Carlo Whale Clear Unconscious Clear 00:24:02 00:29:37 00:05:35"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Carlo Whale Clear Unconscious Clear 00:24:02 00:29:37 00:05:35"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Carlo Whale Clear"": - textbox: Carlo Whale - button ""Clear"" @@ -855,13 +1479,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:35 - cell: - button - - row ""Increment Decrement Arba Han Clear Timelaps Clear 00:29:37 00:33:13 00:03:36"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Arba Han Clear Timelaps Clear 00:29:37 00:33:13 00:03:36"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Arba Han Clear"": - textbox: Arba Han - button ""Clear"" @@ -878,13 +1500,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:36 - cell: - button - - row ""Increment Decrement Savill Clear Energy Surrounds Clear 00:33:13 00:40:30 00:07:17"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Savill Clear Energy Surrounds Clear 00:33:13 00:40:30 00:07:17"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Savill Clear"": - textbox: Savill - button ""Clear"" @@ -901,13 +1521,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:07:17 - cell: - button - - row ""Increment Decrement Teklix Clear The Tribal Code Clear 00:40:30 00:48:53 00:08:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Teklix Clear The Tribal Code Clear 00:40:30 00:48:53 00:08:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Teklix Clear"": - textbox: Teklix - button ""Clear"" @@ -924,13 +1542,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:08:23 - cell: - button - - row ""Increment Decrement Neuralis Clear I'm Looking for Answers Clear 00:48:53 00:54:34 00:05:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""9"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 9 Neuralis Clear I'm Looking for Answers Clear 00:48:53 00:54:34 00:05:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""9"" - cell ""Neuralis Clear"": - textbox: Neuralis - button ""Clear"" @@ -947,13 +1563,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:41 - cell: - button - - row ""Increment Decrement Nopopstar & Arsia Clear Dirty Moves Clear 00:54:34 01:00:24 00:05:50"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""10"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 10 Nopopstar & Arsia Clear Dirty Moves Clear 00:54:34 01:00:24 00:05:50"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""10"" - cell ""Nopopstar & Arsia Clear"": - textbox: Nopopstar & Arsia - button ""Clear"" @@ -970,13 +1584,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:50 - cell: - button - - row ""Increment Decrement SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Lost (Maze 28 Remix) Clear 01:00:24 01:05:17 00:04:53"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""11"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 11 SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Lost (Maze 28 Remix) Clear 01:00:24 01:05:17 00:04:53"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""11"" - cell ""SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear"": - textbox: SevenEver, Nopopstar, 2JOHN'S & Eugene Jay - button ""Clear"" @@ -993,13 +1605,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:53 - cell: - button - - row ""Increment Decrement SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Lost (Redspace Remix) Clear 01:05:17 01:08:22 00:03:05"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""12"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 12 SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Lost (Redspace Remix) Clear 01:05:17 01:08:22 00:03:05"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""12"" - cell ""SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear"": - textbox: SevenEver, Nopopstar, 2JOHN'S & Eugene Jay - button ""Clear"" @@ -1016,13 +1626,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:05 - cell: - button - - row ""Increment Decrement Gadoz Clear 5d Clear 01:08:22 01:16:34 00:08:12"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""13"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 13 Gadoz Clear 5d Clear 01:08:22 01:16:34 00:08:12"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""13"" - cell ""Gadoz Clear"": - textbox: Gadoz - button ""Clear"" @@ -1039,13 +1647,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:08:12 - cell: - button - - row ""Increment Decrement DJ Danzik Clear Out of Space Clear 01:16:34 01:19:22 00:02:48"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""14"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 14 DJ Danzik Clear Out of Space Clear 01:16:34 01:19:22 00:02:48"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""14"" - cell ""DJ Danzik Clear"": - textbox: DJ Danzik - button ""Clear"" @@ -1062,13 +1668,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:02:48 - cell: - button - - row ""Increment Decrement Enis Çoban Clear Internet Clear 01:19:22 01:25:14 00:05:52"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""15"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 15 Enis Çoban Clear Internet Clear 01:19:22 01:25:14 00:05:52"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""15"" - cell ""Enis Çoban Clear"": - textbox: Enis Çoban - button ""Clear"" @@ -1085,13 +1689,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:52 - cell: - button - - row ""Increment Decrement Cold Mind & Alex Yikker Clear Rage Clear 01:25:14 01:31:33 00:06:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""16"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 16 Cold Mind & Alex Yikker Clear Rage Clear 01:25:14 01:31:33 00:06:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""16"" - cell ""Cold Mind & Alex Yikker Clear"": - textbox: Cold Mind & Alex Yikker - button ""Clear"" @@ -1108,13 +1710,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:19 - cell: - button - - row ""Increment Decrement Maze 28 Clear Sol (JAHAYA Remix) Clear 01:31:33 01:32:51 00:01:18"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""17"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 17 Maze 28 Clear Sol (JAHAYA Remix) Clear 01:31:33 01:32:51 00:01:18"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""17"" - cell ""Maze 28 Clear"": - textbox: Maze 28 - button ""Clear"" @@ -1131,13 +1731,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:01:18 - cell: - button - - row ""Increment Decrement Alex Grafton Clear Hi Baby Clear 01:32:51 01:42:13 00:09:22"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""18"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 18 Alex Grafton Clear Hi Baby Clear 01:32:51 01:42:13 00:09:22"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""18"" - cell ""Alex Grafton Clear"": - textbox: Alex Grafton - button ""Clear"" @@ -1154,13 +1752,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:09:22 - cell: - button - - row ""Increment Decrement Che&Mos & Halo Far Clear Daddy Clear 01:42:13 01:45:22 00:03:09"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""19"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 19 Che&Mos & Halo Far Clear Daddy Clear 01:42:13 01:45:22 00:03:09"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""19"" - cell ""Che&Mos & Halo Far Clear"": - textbox: Che&Mos & Halo Far - button ""Clear"" @@ -1177,13 +1773,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:09 - cell: - button - - row ""Increment Decrement K KARDEN Clear Acid Rain Clear 01:45:22 01:48:40 00:03:18"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""20"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 20 K KARDEN Clear Acid Rain Clear 01:45:22 01:48:40 00:03:18"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""20"" - cell ""K KARDEN Clear"": - textbox: K KARDEN - button ""Clear"" @@ -1200,13 +1794,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:18 - cell: - button - - row ""Increment Decrement Dobrov & Gar1sson Clear Analogic (Redspace Remix) Clear 01:48:40 01:56:40 00:08:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""21"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 21 Dobrov & Gar1sson Clear Analogic (Redspace Remix) Clear 01:48:40 01:56:40 00:08:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""21"" - cell ""Dobrov & Gar1sson Clear"": - textbox: Dobrov & Gar1sson - button ""Clear"" @@ -1223,13 +1815,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:08:00 - cell: - button - - row ""Increment Decrement Gazfluz Clear Vargan Clear 01:56:40 02:00:39 00:03:59"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""22"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 22 Gazfluz Clear Vargan Clear 01:56:40 02:00:39 00:03:59"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""22"" - cell ""Gazfluz Clear"": - textbox: Gazfluz - button ""Clear"" @@ -1246,13 +1836,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:59 - cell: - button - - row ""Increment Decrement SHKAPOV Clear Control Clear 02:00:39 02:06:47 00:06:08"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""23"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 23 SHKAPOV Clear Control Clear 02:00:39 02:06:47 00:06:08"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""23"" - cell ""SHKAPOV Clear"": - textbox: SHKAPOV - button ""Clear"" @@ -1269,13 +1857,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:08 - cell: - button - - row ""Increment Decrement QazaQ Clear On the Line Clear 02:06:47 02:09:09 00:02:22"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""24"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 24 QazaQ Clear On the Line Clear 02:06:47 02:09:09 00:02:22"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""24"" - cell ""QazaQ Clear"": - textbox: QazaQ - button ""Clear"" @@ -1292,13 +1878,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:02:22 - cell: - button - - row ""Increment Decrement Alex Schaufel Clear Elizabeth (Larsun Hesh Remix) Clear 02:09:09 02:14:18 00:05:09"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""25"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 25 Alex Schaufel Clear Elizabeth (Larsun Hesh Remix) Clear 02:09:09 02:14:18 00:05:09"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""25"" - cell ""Alex Schaufel Clear"": - textbox: Alex Schaufel - button ""Clear"" @@ -1315,13 +1899,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:09 - cell: - button - - row ""Increment Decrement Oiro Clear Just Business Clear 02:14:18 02:19:22 00:05:04"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""26"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 26 Oiro Clear Just Business Clear 02:14:18 02:19:22 00:05:04"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""26"" - cell ""Oiro Clear"": - textbox: Oiro - button ""Clear"" @@ -1338,13 +1920,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:04 - cell: - button - - row ""Increment Decrement Molex Clear Mind Split (Redspace Remix) Clear 02:19:22 02:27:48 00:08:26"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""27"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 27 Molex Clear Mind Split (Redspace Remix) Clear 02:19:22 02:27:48 00:08:26"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""27"" - cell ""Molex Clear"": - textbox: Molex - button ""Clear"" @@ -1361,13 +1941,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:08:26 - cell: - button - - row ""Increment Decrement SOLI Clear Give Me Your Mind Clear 02:27:48 02:33:48 00:06:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""28"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 28 SOLI Clear Give Me Your Mind Clear 02:27:48 02:33:48 00:06:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""28"" - cell ""SOLI Clear"": - textbox: SOLI - button ""Clear"" @@ -1384,13 +1962,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:00 - cell: - button - - row ""Increment Decrement MANDU Clear Jacky Clear 02:33:48 02:39:05 00:05:17"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""29"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 29 MANDU Clear Jacky Clear 02:33:48 02:39:05 00:05:17"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""29"" - cell ""MANDU Clear"": - textbox: MANDU - button ""Clear"" @@ -1407,13 +1983,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:17 - cell: - button - - row ""Increment Decrement SOLI Clear Spacetoon Clear 02:39:05 02:41:31 00:02:26"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""30"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 30 SOLI Clear Spacetoon Clear 02:39:05 02:41:31 00:02:26"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""30"" - cell ""SOLI Clear"": - textbox: SOLI - button ""Clear"" @@ -1430,13 +2004,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:02:26 - cell: - button - - row ""Increment Decrement Sasha Fashion Clear Moqton Clear 02:41:31 02:50:18 00:08:47"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""31"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 31 Sasha Fashion Clear Moqton Clear 02:41:31 02:50:18 00:08:47"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""31"" - cell ""Sasha Fashion Clear"": - textbox: Sasha Fashion - button ""Clear"" @@ -1453,13 +2025,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:08:47 - cell: - button - - row ""Increment Decrement NAASA Clear Poison Clear 02:50:18 02:56:45 00:06:27"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""32"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 32 NAASA Clear Poison Clear 02:50:18 02:56:45 00:06:27"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""32"" - cell ""NAASA Clear"": - textbox: NAASA - button ""Clear"" @@ -1476,13 +2046,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:27 - cell: - button - - row ""Increment Decrement Nopopstar, 2JOHN'S & Eugene Jay Clear Nightlong Clear 02:56:45 02:59:14 00:02:29"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""33"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 33 Nopopstar, 2JOHN'S & Eugene Jay Clear Nightlong Clear 02:56:45 02:59:14 00:02:29"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""33"" - cell ""Nopopstar, 2JOHN'S & Eugene Jay Clear"": - textbox: Nopopstar, 2JOHN'S & Eugene Jay - button ""Clear"" @@ -1499,13 +2067,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:02:29 - cell: - button - - row ""Increment Decrement Skillz jay Clear Choir Clear 02:59:14 03:05:49 00:06:35"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""34"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 34 Skillz jay Clear Choir Clear 02:59:14 03:05:49 00:06:35"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""34"" - cell ""Skillz jay Clear"": - textbox: Skillz jay - button ""Clear"" @@ -1522,13 +2088,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:35 - cell: - button - - row ""Increment Decrement Kovax Clear Controller Clear 03:05:49 03:08:31 00:02:42"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""35"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 35 Kovax Clear Controller Clear 03:05:49 03:08:31 00:02:42"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""35"" - cell ""Kovax Clear"": - textbox: Kovax - button ""Clear"" @@ -1545,13 +2109,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:02:42 - cell: - button - - row ""Increment Decrement Mumboi Clear Just a Beat Clear 03:08:31 03:15:18 00:06:47"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""36"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 36 Mumboi Clear Just a Beat Clear 03:08:31 03:15:18 00:06:47"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""36"" - cell ""Mumboi Clear"": - textbox: Mumboi - button ""Clear"" @@ -1568,13 +2130,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:47 - cell: - button - - row ""Increment Decrement Eclept Clear Sprut Clear 03:15:18 03:20:35 00:05:17"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""37"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 37 Eclept Clear Sprut Clear 03:15:18 03:20:35 00:05:17"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""37"" - cell ""Eclept Clear"": - textbox: Eclept - button ""Clear"" @@ -1591,13 +2151,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:17 - cell: - button - - row ""Increment Decrement Rudensky Clear Dark Escort Clear 03:20:35 03:25:02 00:04:27"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""38"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 38 Rudensky Clear Dark Escort Clear 03:20:35 03:25:02 00:04:27"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""38"" - cell ""Rudensky Clear"": - textbox: Rudensky - button ""Clear"" @@ -1614,13 +2172,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:27 - cell: - button - - row ""Increment Decrement Alexey Union, Kinky Sound & KOCHETOV Clear Connected Clear 03:25:02 03:32:30 00:07:28"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""39"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 39 Alexey Union, Kinky Sound & KOCHETOV Clear Connected Clear 03:25:02 03:32:30 00:07:28"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""39"" - cell ""Alexey Union, Kinky Sound & KOCHETOV Clear"": - textbox: Alexey Union, Kinky Sound & KOCHETOV - button ""Clear"" @@ -1637,13 +2193,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:07:28 - cell: - button - - row ""Increment Decrement ANMA (MD) Clear Space Yoda (Snyl Remix) Clear 03:32:30 03:37:53 00:05:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""40"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 40 ANMA (MD) Clear Space Yoda (Snyl Remix) Clear 03:32:30 03:37:53 00:05:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""40"" - cell ""ANMA (MD) Clear"": - textbox: ANMA (MD) - button ""Clear"" @@ -1660,13 +2214,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:23 - cell: - button - - row ""Increment Decrement Inache Clear Andale (MONTA (TN) Remix) Clear 03:37:53"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""41"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 41 Inache Clear Andale (MONTA (TN) Remix) Clear 03:37:53"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""41"" - cell ""Inache Clear"": - textbox: Inache - button ""Clear"" @@ -1698,42 +2250,22 @@ public async Task Open_ShouldImportProject_WhenUsingProjectfile() await Expect(detailView.CuesheetTitleInput).ToHaveValueAsync("Sample CD Title"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Sample Artist 1 Clear"": - textbox: Sample Artist 1 - button ""Clear"" @@ -1749,13 +2281,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""00:05:00"": - textbox: 00:05:00 - cell - - row ""Increment Decrement Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell ""Sample Artist 2 Clear"": - textbox: Sample Artist 2 - button ""Clear"" @@ -1772,13 +2302,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:23 - cell: - button - - row ""Increment Decrement Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Sample Artist 3 Clear"": - textbox: Sample Artist 3 - button ""Clear"" @@ -1795,13 +2323,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:31 - cell: - button - - row ""Increment Decrement Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""Sample Artist 4 Clear"": - textbox: Sample Artist 4 - button ""Clear"" @@ -1818,13 +2344,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:19 - cell: - button - - row ""Increment Decrement Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Sample Artist 5 Clear"": - textbox: Sample Artist 5 - button ""Clear"" @@ -1841,13 +2365,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:41 - cell: - button - - row ""Increment Decrement Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Sample Artist 6 Clear"": - textbox: Sample Artist 6 - button ""Clear"" @@ -1864,13 +2386,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:07:00 - cell: - button - - row ""Increment Decrement Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Sample Artist 7 Clear"": - textbox: Sample Artist 7 - button ""Clear"" @@ -1887,13 +2407,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:14:00 - cell: - button - - row ""Increment Decrement Sample Artist 8 Clear Sample Title 8 Clear 00:45:54"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Sample Artist 8 Clear Sample Title 8 Clear 00:45:54"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Sample Artist 8 Clear"": - textbox: Sample Artist 8 - button ""Clear"" @@ -1927,42 +2445,22 @@ public async Task Open_ShouldImportCuesheet_WhenUsingCuesheetfile() await Expect(detailView.CuesheetTitleInput).ToHaveValueAsync("Sample CD Title"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Sample Artist 1 Clear"": - textbox: Sample Artist 1 - button ""Clear"" @@ -1978,13 +2476,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""00:05:00"": - textbox: 00:05:00 - cell - - row ""Increment Decrement Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell ""Sample Artist 2 Clear"": - textbox: Sample Artist 2 - button ""Clear"" @@ -2001,13 +2497,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:23 - cell: - button - - row ""Increment Decrement Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Sample Artist 3 Clear"": - textbox: Sample Artist 3 - button ""Clear"" @@ -2024,13 +2518,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:31 - cell: - button - - row ""Increment Decrement Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""Sample Artist 4 Clear"": - textbox: Sample Artist 4 - button ""Clear"" @@ -2047,13 +2539,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:19 - cell: - button - - row ""Increment Decrement Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Sample Artist 5 Clear"": - textbox: Sample Artist 5 - button ""Clear"" @@ -2070,13 +2560,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:41 - cell: - button - - row ""Increment Decrement Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Sample Artist 6 Clear"": - textbox: Sample Artist 6 - button ""Clear"" @@ -2093,13 +2581,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:07:00 - cell: - button - - row ""Increment Decrement Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Sample Artist 7 Clear"": - textbox: Sample Artist 7 - button ""Clear"" @@ -2116,13 +2602,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:14:00 - cell: - button - - row ""Increment Decrement Sample Artist 8 Clear Sample Title 8 Clear 00:45:54"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Sample Artist 8 Clear Sample Title 8 Clear 00:45:54"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Sample Artist 8 Clear"": - textbox: Sample Artist 8 - button ""Clear"" @@ -2156,42 +2640,22 @@ public async Task Import_ShouldImportTracks_WhenUsingText() await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Sample Artist 1 Clear Sample Title 1 Clear 00:00:00 00:05:00 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Sample Artist 1 Clear"": - textbox: Sample Artist 1 - button ""Clear"" @@ -2207,13 +2671,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""00:05:00"": - textbox: 00:05:00 - cell - - row ""Increment Decrement Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Sample Artist 2 Clear Sample Title 2 Clear 00:05:00 00:09:23 00:04:23"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell ""Sample Artist 2 Clear"": - textbox: Sample Artist 2 - button ""Clear"" @@ -2230,13 +2692,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:23 - cell: - button - - row ""Increment Decrement Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Sample Artist 3 Clear Sample Title 3 Clear 00:09:23 00:15:54 00:06:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Sample Artist 3 Clear"": - textbox: Sample Artist 3 - button ""Clear"" @@ -2253,13 +2713,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:31 - cell: - button - - row ""Increment Decrement Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 Sample Artist 4 Clear Sample Title 4 Clear 00:15:54 00:20:13 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""Sample Artist 4 Clear"": - textbox: Sample Artist 4 - button ""Clear"" @@ -2276,13 +2734,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:19 - cell: - button - - row ""Increment Decrement Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Sample Artist 5 Clear Sample Title 5 Clear 00:20:13 00:24:54 00:04:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Sample Artist 5 Clear"": - textbox: Sample Artist 5 - button ""Clear"" @@ -2299,13 +2755,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:41 - cell: - button - - row ""Increment Decrement Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Sample Artist 6 Clear Sample Title 6 Clear 00:24:54 00:31:54 00:07:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Sample Artist 6 Clear"": - textbox: Sample Artist 6 - button ""Clear"" @@ -2322,13 +2776,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:07:00 - cell: - button - - row ""Increment Decrement Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Sample Artist 7 Clear Sample Title 7 Clear 00:31:54 00:45:54 00:14:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Sample Artist 7 Clear"": - textbox: Sample Artist 7 - button ""Clear"" @@ -2345,13 +2797,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:14:00 - cell: - button - - row ""Increment Decrement Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Sample Artist 8 Clear Sample Title 8 Clear 00:45:54 01:15:54 00:30:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Sample Artist 8 Clear"": - textbox: Sample Artist 8 - button ""Clear"" diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs index da4133d1..99e78b87 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs @@ -39,7 +39,7 @@ public async Task Record_ShouldRecordTracks_WhenTracksAdded() await viewModes.SwitchView("Detail view"); await detailView.SelectTracksAsync([1]); await detailView.EditSelectedTracksModalAsync(); - await Expect(TestPage.GetByRole(AriaRole.Checkbox, new() { Name = "Link to previous track" })).ToBeCheckedAsync(); + await Expect(TestPage.GetByRole(AriaRole.Switch, new() { Name = "Link to previous track" })).ToBeCheckedAsync(); } } } diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/TracingTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/TracingTest.cs index 0067adb2..a6c48845 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/TracingTest.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/TracingTest.cs @@ -38,36 +38,34 @@ public async Task UndoRedo_ShouldRestoreTrackState_WhenUndoAndRedoAreUsed() await bar.UndoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row: + - row ""Select row 1 00:00:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" + - cell: + - textbox + - button + - cell: + - textbox + - button + - cell ""00:00:00"": + - textbox: 00:00:00 + - cell: + - textbox + - cell: + - textbox - cell - rowgroup: - row"); @@ -76,42 +74,22 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta await bar.RedoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Test Artist 1 Clear Test Title 1 Clear 00:00:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Test Artist 1 Clear Test Title 1 Clear 00:00:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Test Artist 1 Clear"": - textbox: Test Artist 1 - button ""Clear"" @@ -132,42 +110,22 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta await detailView.EditTrackAsync("Mozart", "Eine kleine Nachtmusik"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Mozart Clear Eine kleine Nachtmusik Clear 00:00:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Mozart Clear Eine kleine Nachtmusik Clear 00:00:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Mozart Clear"": - textbox: Mozart - button ""Clear"" @@ -188,42 +146,22 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta await bar.UndoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Mozart Clear Test Title 1 Clear 00:00:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Mozart Clear Test Title 1 Clear 00:00:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Mozart Clear"": - textbox: Mozart - button ""Clear"" @@ -254,42 +192,22 @@ public async Task UndoRedo_ShouldRestoreTrackState_WhenModalEdit() await detailView.EditTracksModalAsync("Test Track Artist 1", "Test Track Title 1", "00:02:23", ["channel audio (4CH)", "Serial copy management system"]); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox [checked] - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" [checked] + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Test Track Artist 1 Clear Test Track Title 1 Clear 00:00:00 00:02:23 00:02:23"": - - cell: - - checkbox [checked] - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Test Track Artist 1 Clear Test Track Title 1 Clear 00:00:00 00:02:23 00:02:23"" [selected]: + - cell ""Select row"": + - checkbox ""Select row"" [checked] + - text: Select row + - cell ""1"" - cell ""Test Track Artist 1 Clear"": - textbox: Test Track Artist 1 - button ""Clear"" @@ -310,42 +228,22 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta await bar.UndoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox [checked] - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" [checked] + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement 00:00:00"": - - cell: - - checkbox [checked] - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 00:00:00 End has no value! Length has no value!"" [selected]: + - cell ""Select row"": + - checkbox ""Select row"" [checked] + - text: Select row + - cell ""1"" - cell: - textbox - button @@ -354,52 +252,34 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - button - cell ""00:00:00"": - textbox: 00:00:00 - - cell: + - cell ""End has no value!"": - textbox - - cell: + - text: End has no value! + - cell ""Length has no value!"": - textbox + - text: Length has no value! - cell - rowgroup: - row"); await bar.RedoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox [checked] - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" [checked] + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Test Track Artist 1 Clear Test Track Title 1 Clear 00:00:00 00:02:23 00:02:23"": - - cell: - - checkbox [checked] - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Test Track Artist 1 Clear Test Track Title 1 Clear 00:00:00 00:02:23 00:02:23"" [selected]: + - cell ""Select row"": + - checkbox ""Select row"" [checked] + - text: Select row + - cell ""1"" - cell ""Test Track Artist 1 Clear"": - textbox: Test Track Artist 1 - button ""Clear"" @@ -443,42 +323,22 @@ public async Task UndoRedo_ShouldRestoreCuesheet_WhenUsingImport() await Expect(importView.CatalogueNumberInput).ToHaveValueAsync("01 23456 78912 3"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: - rowgroup: - - row ""# Sort Column options Artist Sort Column options Title Sort Column options Begin Sort Column options End Sort Column options Length Sort Column options Status"": - - columnheader: - - checkbox - - columnheader ""# Sort Column options"": - - text: ""#"" - - button ""Sort"" - - button ""Column options"" - - columnheader ""Artist Sort Column options"": - - text: Artist - - button ""Sort"" - - button ""Column options"" - - columnheader ""Title Sort Column options"": - - text: Title - - button ""Sort"" - - button ""Column options"" - - columnheader ""Begin Sort Column options"": - - text: Begin - - button ""Sort"" - - button ""Column options"" - - columnheader ""End Sort Column options"": - - text: End - - button ""Sort"" - - button ""Column options"" - - columnheader ""Length Sort Column options"": - - text: Length - - button ""Sort"" - - button ""Column options"" + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" - columnheader ""Status"" - rowgroup: - - row ""Increment Decrement Adriatique Clear X. Clear 00:00:00 00:05:24.2500000 00:05:24.2500000"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 1 Adriatique Clear X. Clear 00:00:00 00:05:24.2500000 00:05:24.2500000"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" - cell ""Adriatique Clear"": - textbox: Adriatique - button ""Clear"" @@ -494,13 +354,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""00:05:24.2500000"": - textbox: 00:05:24.2500000 - cell - - row ""Increment Decrement Third Harmony Clear Fears And Dreams (Original Mix) Clear 00:05:24.2500000 00:10:39 00:05:14.7500000"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 2 Third Harmony Clear Fears And Dreams (Original Mix) Clear 00:05:24.2500000 00:10:39 00:05:14.7500000"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" - cell ""Third Harmony Clear"": - textbox: Third Harmony - button ""Clear"" @@ -517,13 +375,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:14.7500000 - cell: - button - - row ""Increment Decrement Dele Sosimi Afrobeat Orchestra Clear Too Much Information (Laolu Remix; Edit) Clear 00:10:39 00:17:06 00:06:27"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 3 Dele Sosimi Afrobeat Orchestra Clear Too Much Information (Laolu Remix; Edit) Clear 00:10:39 00:17:06 00:06:27"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""3"" - cell ""Dele Sosimi Afrobeat Orchestra Clear"": - textbox: Dele Sosimi Afrobeat Orchestra - button ""Clear"" @@ -540,13 +396,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:27 - cell: - button - - row ""Increment Decrement Edem, Govan Clear Ankh (Onetwo MX Remix) Clear 00:17:06 00:23:21 00:06:15"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 4 Edem, Govan Clear Ankh (Onetwo MX Remix) Clear 00:17:06 00:23:21 00:06:15"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""4"" - cell ""Edem, Govan Clear"": - textbox: Edem, Govan - button ""Clear"" @@ -563,13 +417,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:15 - cell: - button - - row ""Increment Decrement Jody Wisternoff Clear For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear 00:23:21 00:29:02 00:05:41"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 5 Jody Wisternoff Clear For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear 00:23:21 00:29:02 00:05:41"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""5"" - cell ""Jody Wisternoff Clear"": - textbox: Jody Wisternoff - button ""Clear"" @@ -586,13 +438,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:41 - cell: - button - - row ""Increment Decrement Einmusik Clear Bead (Original Mix) Clear 00:29:02 00:34:27 00:05:25"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 6 Einmusik Clear Bead (Original Mix) Clear 00:29:02 00:34:27 00:05:25"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""6"" - cell ""Einmusik Clear"": - textbox: Einmusik - button ""Clear"" @@ -609,13 +459,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:25 - cell: - button - - row ""Increment Decrement Sebastien Leger Clear La Danse du Scorpion Clear 00:34:27 00:40:59 00:06:32"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 7 Sebastien Leger Clear La Danse du Scorpion Clear 00:34:27 00:40:59 00:06:32"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""7"" - cell ""Sebastien Leger Clear"": - textbox: Sebastien Leger - button ""Clear"" @@ -632,13 +480,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:32 - cell: - button - - row ""Increment Decrement Paul Thomas & Solid Stone Clear La Bombo (Solid Stone Remix) Clear 00:40:59 00:46:19 00:05:20"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 8 Paul Thomas & Solid Stone Clear La Bombo (Solid Stone Remix) Clear 00:40:59 00:46:19 00:05:20"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""8"" - cell ""Paul Thomas & Solid Stone Clear"": - textbox: Paul Thomas & Solid Stone - button ""Clear"" @@ -655,13 +501,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:20 - cell: - button - - row ""Increment Decrement GusGus Clear Crossfade (Maceo Plex Mix) Clear 00:46:19 00:52:20 00:06:01"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""9"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 9 GusGus Clear Crossfade (Maceo Plex Mix) Clear 00:46:19 00:52:20 00:06:01"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""9"" - cell ""GusGus Clear"": - textbox: GusGus - button ""Clear"" @@ -678,13 +522,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:01 - cell: - button - - row ""Increment Decrement Klangkarussell Clear Time (Original Mix) Clear 00:52:20 00:56:19 00:03:59"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""10"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 10 Klangkarussell Clear Time (Original Mix) Clear 00:52:20 00:56:19 00:03:59"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""10"" - cell ""Klangkarussell Clear"": - textbox: Klangkarussell - button ""Clear"" @@ -701,13 +543,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:59 - cell: - button - - row ""Increment Decrement Anysense & Un:said Clear Missing Path (Original Mix) Clear 00:56:19 01:01:41 00:05:22"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""11"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 11 Anysense & Un:said Clear Missing Path (Original Mix) Clear 00:56:19 01:01:41 00:05:22"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""11"" - cell ""Anysense & Un:said Clear"": - textbox: Anysense & Un:said - button ""Clear"" @@ -724,13 +564,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:22 - cell: - button - - row ""Increment Decrement Space Food Clear Bombay Clear 01:01:41 01:06:33 00:04:52"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""12"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 12 Space Food Clear Bombay Clear 01:01:41 01:06:33 00:04:52"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""12"" - cell ""Space Food Clear"": - textbox: Space Food - button ""Clear"" @@ -747,13 +585,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:52 - cell: - button - - row ""Increment Decrement SHDW & Obscure Shape Clear Wächter der Nacht (Original Mix) Clear 01:06:33 01:11:04 00:04:31"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""13"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 13 SHDW & Obscure Shape Clear Wächter der Nacht (Original Mix) Clear 01:06:33 01:11:04 00:04:31"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""13"" - cell ""SHDW & Obscure Shape Clear"": - textbox: SHDW & Obscure Shape - button ""Clear"" @@ -770,13 +606,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:31 - cell: - button - - row ""Increment Decrement HOSH Clear Karma Clear 01:11:04 01:15:28 00:04:24"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""14"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 14 HOSH Clear Karma Clear 01:11:04 01:15:28 00:04:24"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""14"" - cell ""HOSH Clear"": - textbox: HOSH - button ""Clear"" @@ -793,13 +627,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:24 - cell: - button - - row ""Increment Decrement Alexey Union Clear Olympia (Original Mix) Clear 01:15:28 01:21:08 00:05:40"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""15"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 15 Alexey Union Clear Olympia (Original Mix) Clear 01:15:28 01:21:08 00:05:40"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""15"" - cell ""Alexey Union Clear"": - textbox: Alexey Union - button ""Clear"" @@ -816,13 +648,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:40 - cell: - button - - row ""Increment Decrement Paul Taylor Clear Afterglow Clear 01:21:08 01:25:38 00:04:30"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""16"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 16 Paul Taylor Clear Afterglow Clear 01:21:08 01:25:38 00:04:30"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""16"" - cell ""Paul Taylor Clear"": - textbox: Paul Taylor - button ""Clear"" @@ -839,13 +669,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:30 - cell: - button - - row ""Increment Decrement Philter Clear Stranger Clear 01:25:38 01:31:52 00:06:14"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""17"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 17 Philter Clear Stranger Clear 01:25:38 01:31:52 00:06:14"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""17"" - cell ""Philter Clear"": - textbox: Philter - button ""Clear"" @@ -862,13 +690,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:14 - cell: - button - - row ""Increment Decrement Skizologic Clear Hypersphere (Original Mix) Clear 01:31:52 01:36:40 00:04:48"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""18"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 18 Skizologic Clear Hypersphere (Original Mix) Clear 01:31:52 01:36:40 00:04:48"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""18"" - cell ""Skizologic Clear"": - textbox: Skizologic - button ""Clear"" @@ -885,13 +711,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:48 - cell: - button - - row ""Increment Decrement Thomas Schumacher, Caitlin Clear All of You (Remix) Clear 01:36:40 01:42:16 00:05:36"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""19"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 19 Thomas Schumacher, Caitlin Clear All of You (Remix) Clear 01:36:40 01:42:16 00:05:36"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""19"" - cell ""Thomas Schumacher, Caitlin Clear"": - textbox: Thomas Schumacher, Caitlin - button ""Clear"" @@ -908,13 +732,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:36 - cell: - button - - row ""Increment Decrement A. Skomoroh Clear White Horse Conquest (Original Mix) Clear 01:42:16 01:47:04 00:04:48"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""20"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 20 A. Skomoroh Clear White Horse Conquest (Original Mix) Clear 01:42:16 01:47:04 00:04:48"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""20"" - cell ""A. Skomoroh Clear"": - textbox: A. Skomoroh - button ""Clear"" @@ -931,13 +753,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:48 - cell: - button - - row ""Increment Decrement Patrik Berg Clear Bright (Original Mix) Clear 01:47:04 01:52:37 00:05:33"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""21"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 21 Patrik Berg Clear Bright (Original Mix) Clear 01:47:04 01:52:37 00:05:33"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""21"" - cell ""Patrik Berg Clear"": - textbox: Patrik Berg - button ""Clear"" @@ -954,13 +774,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:33 - cell: - button - - row ""Increment Decrement Hidden Empire Clear Bengal Clear 01:52:37 01:58:05 00:05:28"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""22"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 22 Hidden Empire Clear Bengal Clear 01:52:37 01:58:05 00:05:28"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""22"" - cell ""Hidden Empire Clear"": - textbox: Hidden Empire - button ""Clear"" @@ -977,13 +795,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:28 - cell: - button - - row ""Increment Decrement Mario Ochoa Clear Levitate Clear 01:58:05 02:03:00 00:04:55"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""23"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 23 Mario Ochoa Clear Levitate Clear 01:58:05 02:03:00 00:04:55"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""23"" - cell ""Mario Ochoa Clear"": - textbox: Mario Ochoa - button ""Clear"" @@ -1000,13 +816,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:55 - cell: - button - - row ""Increment Decrement Raul Facio Clear Eyes Wide Shut (Original Mix) Clear 02:03:00 02:08:21 00:05:21"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""24"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 24 Raul Facio Clear Eyes Wide Shut (Original Mix) Clear 02:03:00 02:08:21 00:05:21"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""24"" - cell ""Raul Facio Clear"": - textbox: Raul Facio - button ""Clear"" @@ -1023,13 +837,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:21 - cell: - button - - row ""Increment Decrement Soolver Clear Regular (Original Mix) Clear 02:08:21 02:14:31 00:06:10"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""25"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 25 Soolver Clear Regular (Original Mix) Clear 02:08:21 02:14:31 00:06:10"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""25"" - cell ""Soolver Clear"": - textbox: Soolver - button ""Clear"" @@ -1046,13 +858,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:06:10 - cell: - button - - row ""Increment Decrement Weska Clear EQ64 (Original Mix) Clear 02:14:31 02:18:35 00:04:04"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""26"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 26 Weska Clear EQ64 (Original Mix) Clear 02:14:31 02:18:35 00:04:04"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""26"" - cell ""Weska Clear"": - textbox: Weska - button ""Clear"" @@ -1069,13 +879,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:04 - cell: - button - - row ""Increment Decrement Tempo Giusto Clear The Fall (Extended Mix) Clear 02:18:35 02:24:12 00:05:37"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""27"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 27 Tempo Giusto Clear The Fall (Extended Mix) Clear 02:18:35 02:24:12 00:05:37"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""27"" - cell ""Tempo Giusto Clear"": - textbox: Tempo Giusto - button ""Clear"" @@ -1092,13 +900,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:37 - cell: - button - - row ""Increment Decrement Vlind & Asteroid & Gary Leroy Clear Trinity (Extended Mix) Clear 02:24:12 02:29:38 00:05:26"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""28"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 28 Vlind & Asteroid & Gary Leroy Clear Trinity (Extended Mix) Clear 02:24:12 02:29:38 00:05:26"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""28"" - cell ""Vlind & Asteroid & Gary Leroy Clear"": - textbox: Vlind & Asteroid & Gary Leroy - button ""Clear"" @@ -1115,13 +921,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:26 - cell: - button - - row ""Increment Decrement Astral Legacy Clear Vaveyla (Original Mix) Clear 02:29:38 02:32:52 00:03:14"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""29"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 29 Astral Legacy Clear Vaveyla (Original Mix) Clear 02:29:38 02:32:52 00:03:14"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""29"" - cell ""Astral Legacy Clear"": - textbox: Astral Legacy - button ""Clear"" @@ -1138,13 +942,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:14 - cell: - button - - row ""Increment Decrement Gerrox Clear Chakra (Original Mix) Clear 02:32:52 02:37:00 00:04:08"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""30"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 30 Gerrox Clear Chakra (Original Mix) Clear 02:32:52 02:37:00 00:04:08"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""30"" - cell ""Gerrox Clear"": - textbox: Gerrox - button ""Clear"" @@ -1161,13 +963,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:08 - cell: - button - - row ""Increment Decrement Charlotte De Witte Clear Pattern Clear 02:37:00 02:41:55 00:04:55"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""31"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 31 Charlotte De Witte Clear Pattern Clear 02:37:00 02:41:55 00:04:55"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""31"" - cell ""Charlotte De Witte Clear"": - textbox: Charlotte De Witte - button ""Clear"" @@ -1184,13 +984,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:55 - cell: - button - - row ""Increment Decrement Space Food Clear Amabey Clear 02:41:55 02:46:55 00:05:00"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""32"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 32 Space Food Clear Amabey Clear 02:41:55 02:46:55 00:05:00"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""32"" - cell ""Space Food Clear"": - textbox: Space Food - button ""Clear"" @@ -1207,13 +1005,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:05:00 - cell: - button - - row ""Increment Decrement ARTBAT Clear Papilion (Original Mix) Clear 02:46:55 02:51:13 00:04:18"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""33"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 33 ARTBAT Clear Papilion (Original Mix) Clear 02:46:55 02:51:13 00:04:18"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""33"" - cell ""ARTBAT Clear"": - textbox: ARTBAT - button ""Clear"" @@ -1230,13 +1026,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:18 - cell: - button - - row ""Increment Decrement PETER PAHN Clear Enjoy Infinity (Original Mix) Clear 02:51:13 02:56:08 00:04:55"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""34"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 34 PETER PAHN Clear Enjoy Infinity (Original Mix) Clear 02:51:13 02:56:08 00:04:55"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""34"" - cell ""PETER PAHN Clear"": - textbox: PETER PAHN - button ""Clear"" @@ -1253,13 +1047,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:55 - cell: - button - - row ""Increment Decrement Solitek Clear Instinct (Original Mix) Clear 02:56:08 03:00:57 00:04:49"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""35"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 35 Solitek Clear Instinct (Original Mix) Clear 02:56:08 03:00:57 00:04:49"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""35"" - cell ""Solitek Clear"": - textbox: Solitek - button ""Clear"" @@ -1276,13 +1068,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:49 - cell: - button - - row ""Increment Decrement Veerus Clear Heavy Clear 03:00:57 03:05:19 00:04:22"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""36"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 36 Veerus Clear Heavy Clear 03:00:57 03:05:19 00:04:22"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""36"" - cell ""Veerus Clear"": - textbox: Veerus - button ""Clear"" @@ -1299,13 +1089,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:22 - cell: - button - - row ""Increment Decrement Secret Cinema & Reinier Zonneveld Clear Pain Thing (Original Mix) Clear 03:05:19 03:09:38 00:04:19"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""37"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 37 Secret Cinema & Reinier Zonneveld Clear Pain Thing (Original Mix) Clear 03:05:19 03:09:38 00:04:19"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""37"" - cell ""Secret Cinema & Reinier Zonneveld Clear"": - textbox: Secret Cinema & Reinier Zonneveld - button ""Clear"" @@ -1322,13 +1110,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:04:19 - cell: - button - - row ""Increment Decrement Amelie Lens Clear Hypnotized Clear 03:09:38 03:13:13 00:03:35"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""38"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 38 Amelie Lens Clear Hypnotized Clear 03:09:38 03:13:13 00:03:35"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""38"" - cell ""Amelie Lens Clear"": - textbox: Amelie Lens - button ""Clear"" @@ -1345,13 +1131,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox: 00:03:35 - cell: - button - - row ""Increment Decrement Nikolay Kirov Clear Chasing the Sun (Original Mix) Clear 03:13:13"": - - cell: - - checkbox - - cell ""Increment Decrement"": - - spinbutton: ""39"" - - button ""Increment"" - - button ""Decrement"" + - row ""Select row 39 Nikolay Kirov Clear Chasing the Sun (Original Mix) Clear 03:13:13"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""39"" - cell ""Nikolay Kirov Clear"": - textbox: Nikolay Kirov - button ""Clear"" @@ -1368,8 +1152,111 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - textbox - cell: - button + - rowgroup: + - row"); + } + + [TestMethod] + public async Task UndoRedo_ShouldRestoreTrackState_WhenUndoWithAudiofile() + { + var bar = new AppBar(TestPage); + var detailView = new DetailView(TestPage); + await detailView.GotoAsync(); + await detailView.AddTrackAsync(); + await detailView.AudiofileInput.SetInputFilesAsync("Kalimba.mp3"); + await detailView.AddTrackAsync(); + await Expect(bar.UndoButton).ToBeEnabledAsync(); + await Expect(bar.RedoButton).ToBeDisabledAsync(); + await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup: + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" + - columnheader ""Status"" + - rowgroup: + - row ""Select row 1 00:00:00 End has no value! Length has no value!"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" + - cell: + - textbox + - button + - cell: + - textbox + - button + - cell ""00:00:00"": + - textbox: 00:00:00 + - cell ""End has no value!"": + - textbox + - text: End has no value! + - cell ""Length has no value!"": + - textbox + - text: Length has no value! + - cell + - row ""Select row 2 00:05:48.0608330"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""2"" + - cell: + - textbox + - button + - cell: + - textbox + - button + - cell: + - textbox + - cell ""00:05:48.0608330"": + - textbox: 00:05:48.0608330 + - cell: + - textbox + - cell: + - button + - rowgroup: + - row"); + await bar.UndoAsync(); + await Expect(bar.UndoButton).ToBeEnabledAsync(); + await Expect(bar.RedoButton).ToBeEnabledAsync(); + await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup: + - 'row ""Select all rows # Artist Title Begin End Length Status""': + - columnheader ""Select all rows"": + - checkbox ""Select all rows"" + - columnheader ""#"" + - columnheader ""Artist"" + - columnheader ""Title"" + - columnheader ""Begin"" + - columnheader ""End"" + - columnheader ""Length"" + - columnheader ""Status"" + - rowgroup: + - row ""Select row 1 00:00:00 00:05:48.0608330 00:05:48.0608330"": + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""1"" + - cell: + - textbox + - button + - cell: + - textbox + - button + - cell ""00:00:00"": + - textbox: 00:00:00 + - cell ""00:05:48.0608330"": + - textbox: 00:05:48.0608330 + - cell ""00:05:48.0608330"": + - textbox: 00:05:48.0608330 + - cell - rowgroup: - row"); } } -} +} \ No newline at end of file diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ImportTestSmartphone.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ImportTestSmartphone.cs index 57f346d5..129f49ad 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ImportTestSmartphone.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ImportTestSmartphone.cs @@ -33,15 +33,13 @@ public async Task Import_ShouldImportTracks_WhenUsingSampleInputfile() await importView.Analyze(); await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Sample Artist 1 Clear"": - text: Artist - textbox: Sample Artist 1 @@ -62,14 +60,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Length - textbox: 00:05:00 - cell ""Status"" - - row ""# Increment Decrement Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Sample Artist 2 Clear"": - text: Artist - textbox: Sample Artist 2 @@ -92,14 +87,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Sample Artist 3 Clear"": - text: Artist - textbox: Sample Artist 3 @@ -122,14 +114,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Sample Artist 4 Clear"": - text: Artist - textbox: Sample Artist 4 @@ -152,14 +141,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Sample Artist 5 Clear"": - text: Artist - textbox: Sample Artist 5 @@ -182,14 +168,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Sample Artist 6 Clear"": - text: Artist - textbox: Sample Artist 6 @@ -212,14 +195,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sample Artist 7 Clear"": - text: Artist - textbox: Sample Artist 7 @@ -242,14 +222,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Sample Artist 8 Clear"": - text: Artist - textbox: Sample Artist 8 @@ -288,15 +265,13 @@ public async Task Import_ShouldBeEditable_WhenEditingTrack() await importView.EditTracksModalAsync("Sample Title Edited 5"); await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Sample Artist 1 Clear"": - text: Artist - textbox: Sample Artist 1 @@ -317,14 +292,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Length - textbox: 00:05:00 - cell ""Status"" - - row ""# Increment Decrement Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Sample Artist 2 Clear"": - text: Artist - textbox: Sample Artist 2 @@ -347,14 +319,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Sample Artist 3 Clear"": - text: Artist - textbox: Sample Artist 3 @@ -377,14 +346,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Sample Artist 4 Clear"": - text: Artist - textbox: Sample Artist 4 @@ -407,14 +373,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 5 Clear Title Sample Title Edited 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Sample Artist 5 Clear Title Sample Title Edited 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Sample Artist 5 Clear"": - text: Artist - textbox: Sample Artist 5 @@ -437,14 +400,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Sample Artist 6 Clear"": - text: Artist - textbox: Sample Artist 6 @@ -467,14 +427,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sample Artist 7 Clear"": - text: Artist - textbox: Sample Artist 7 @@ -497,14 +454,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Sample Artist 8 Clear"": - text: Artist - textbox: Sample Artist 8 @@ -538,15 +492,13 @@ public async Task Import_ShouldImportTracks_WithSpecialTextfile() await importView.SwitchImportProfileAsync("Textfile (just track data)"); await importView.Analyze(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Adriatique Clear Title X. Clear Begin 00:00:00 End 00:05:24 Length 00:05:24"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Adriatique Clear Title X. Clear Begin 00:00:00 End 00:05:24 Length 00:05:24""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Adriatique Clear"": - text: Artist - textbox: Adriatique @@ -566,14 +518,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:24"": - text: Length - textbox: 00:05:24 - - row ""# Increment Decrement Artist Third Harmony Clear Title Fears And Dreams (Original Mix) Clear Begin 00:05:24 End 00:10:39 Length 00:05:15"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Third Harmony Clear Title Fears And Dreams (Original Mix) Clear Begin 00:05:24 End 00:10:39 Length 00:05:15""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Third Harmony Clear"": - text: Artist - textbox: Third Harmony @@ -593,14 +542,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:15"": - text: Length - textbox: 00:05:15 - - row ""# Increment Decrement Artist Dele Sosimi Afrobeat Orchestra Clear Title Too Much Information (Laolu Remix; Edit) Clear Begin 00:10:39 End 00:17:06 Length 00:06:27"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Dele Sosimi Afrobeat Orchestra Clear Title Too Much Information (Laolu Remix; Edit) Clear Begin 00:10:39 End 00:17:06 Length 00:06:27""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Dele Sosimi Afrobeat Orchestra Clear"": - text: Artist - textbox: Dele Sosimi Afrobeat Orchestra @@ -620,14 +566,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:06:27"": - text: Length - textbox: 00:06:27 - - row ""# Increment Decrement Artist Edem, Govan Clear Title Ankh (Onetwo MX Remix) Clear Begin 00:17:06 End 00:23:21 Length 00:06:15"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Edem, Govan Clear Title Ankh (Onetwo MX Remix) Clear Begin 00:17:06 End 00:23:21 Length 00:06:15""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Edem, Govan Clear"": - text: Artist - textbox: Edem, Govan @@ -647,14 +590,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:06:15"": - text: Length - textbox: 00:06:15 - - row ""# Increment Decrement Artist Jody Wisternoff Clear Title For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear Begin 00:23:21 End 00:29:02 Length 00:05:41"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Jody Wisternoff Clear Title For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear Begin 00:23:21 End 00:29:02 Length 00:05:41""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Jody Wisternoff Clear"": - text: Artist - textbox: Jody Wisternoff @@ -674,14 +614,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:41"": - text: Length - textbox: 00:05:41 - - row ""# Increment Decrement Artist Einmusik Clear Title Bead (Original Mix) Clear Begin 00:29:02 End 00:34:27 Length 00:05:25"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Einmusik Clear Title Bead (Original Mix) Clear Begin 00:29:02 End 00:34:27 Length 00:05:25""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Einmusik Clear"": - text: Artist - textbox: Einmusik @@ -701,14 +638,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:25"": - text: Length - textbox: 00:05:25 - - row ""# Increment Decrement Artist Sebastien Leger Clear Title La Danse du Scorpion Clear Begin 00:34:27 End 00:40:59 Length 00:06:32"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sebastien Leger Clear Title La Danse du Scorpion Clear Begin 00:34:27 End 00:40:59 Length 00:06:32""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sebastien Leger Clear"": - text: Artist - textbox: Sebastien Leger @@ -728,14 +662,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:06:32"": - text: Length - textbox: 00:06:32 - - row ""# Increment Decrement Artist Paul Thomas & Solid Stone Clear Title La Bombo (Solid Stone Remix) Clear Begin 00:40:59 End 00:46:19 Length 00:05:20"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Paul Thomas & Solid Stone Clear Title La Bombo (Solid Stone Remix) Clear Begin 00:40:59 End 00:46:19 Length 00:05:20""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Paul Thomas & Solid Stone Clear"": - text: Artist - textbox: Paul Thomas & Solid Stone @@ -755,14 +686,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:20"": - text: Length - textbox: 00:05:20 - - row ""# Increment Decrement Artist GusGus Clear Title Crossfade (Maceo Plex Mix) Clear Begin 00:46:19 End 00:52:20 Length 00:06:01"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""9"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 9 Artist GusGus Clear Title Crossfade (Maceo Plex Mix) Clear Begin 00:46:19 End 00:52:20 Length 00:06:01""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 9"" - cell ""Artist GusGus Clear"": - text: Artist - textbox: GusGus @@ -782,14 +710,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:06:01"": - text: Length - textbox: 00:06:01 - - row ""# Increment Decrement Artist Klangkarussell Clear Title Time (Original Mix) Clear Begin 00:52:20 End 00:56:19 Length 00:03:59"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""10"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 10 Artist Klangkarussell Clear Title Time (Original Mix) Clear Begin 00:52:20 End 00:56:19 Length 00:03:59""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 10"" - cell ""Artist Klangkarussell Clear"": - text: Artist - textbox: Klangkarussell @@ -809,14 +734,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:03:59"": - text: Length - textbox: 00:03:59 - - row ""# Increment Decrement Artist Anysense & Un:said Clear Title Missing Path (Original Mix) Clear Begin 00:56:19 End 01:01:41 Length 00:05:22"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""11"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 11 Artist Anysense & Un:said Clear Title Missing Path (Original Mix) Clear Begin 00:56:19 End 01:01:41 Length 00:05:22""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 11"" - cell ""Artist Anysense & Un:said Clear"": - text: Artist - textbox: Anysense & Un:said @@ -836,14 +758,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:22"": - text: Length - textbox: 00:05:22 - - row ""# Increment Decrement Artist Space Food Clear Title Bombay Clear Begin 01:01:41 End 01:06:33 Length 00:04:52"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""12"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 12 Artist Space Food Clear Title Bombay Clear Begin 01:01:41 End 01:06:33 Length 00:04:52""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 12"" - cell ""Artist Space Food Clear"": - text: Artist - textbox: Space Food @@ -863,14 +782,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:52"": - text: Length - textbox: 00:04:52 - - row ""# Increment Decrement Artist SHDW & Obscure Shape Clear Title Wächter der Nacht (Original Mix) Clear Begin 01:06:33 End 01:11:04 Length 00:04:31"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""13"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 13 Artist SHDW & Obscure Shape Clear Title Wächter der Nacht (Original Mix) Clear Begin 01:06:33 End 01:11:04 Length 00:04:31""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 13"" - cell ""Artist SHDW & Obscure Shape Clear"": - text: Artist - textbox: SHDW & Obscure Shape @@ -890,14 +806,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:31"": - text: Length - textbox: 00:04:31 - - row ""# Increment Decrement Artist HOSH Clear Title Karma Clear Begin 01:11:04 End 01:15:28 Length 00:04:24"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""14"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 14 Artist HOSH Clear Title Karma Clear Begin 01:11:04 End 01:15:28 Length 00:04:24""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 14"" - cell ""Artist HOSH Clear"": - text: Artist - textbox: HOSH @@ -917,14 +830,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:24"": - text: Length - textbox: 00:04:24 - - row ""# Increment Decrement Artist Alexey Union Clear Title Olympia (Original Mix) Clear Begin 01:15:28 End 01:21:08 Length 00:05:40"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""15"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 15 Artist Alexey Union Clear Title Olympia (Original Mix) Clear Begin 01:15:28 End 01:21:08 Length 00:05:40""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 15"" - cell ""Artist Alexey Union Clear"": - text: Artist - textbox: Alexey Union @@ -944,14 +854,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:40"": - text: Length - textbox: 00:05:40 - - row ""# Increment Decrement Artist Paul Taylor Clear Title Afterglow Clear Begin 01:21:08 End 01:25:38 Length 00:04:30"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""16"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 16 Artist Paul Taylor Clear Title Afterglow Clear Begin 01:21:08 End 01:25:38 Length 00:04:30""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 16"" - cell ""Artist Paul Taylor Clear"": - text: Artist - textbox: Paul Taylor @@ -971,14 +878,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:30"": - text: Length - textbox: 00:04:30 - - row ""# Increment Decrement Artist Philter Clear Title Stranger Clear Begin 01:25:38 End 01:31:52 Length 00:06:14"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""17"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 17 Artist Philter Clear Title Stranger Clear Begin 01:25:38 End 01:31:52 Length 00:06:14""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 17"" - cell ""Artist Philter Clear"": - text: Artist - textbox: Philter @@ -998,14 +902,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:06:14"": - text: Length - textbox: 00:06:14 - - row ""# Increment Decrement Artist Skizologic Clear Title Hypersphere (Original Mix) Clear Begin 01:31:52 End 01:36:40 Length 00:04:48"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""18"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 18 Artist Skizologic Clear Title Hypersphere (Original Mix) Clear Begin 01:31:52 End 01:36:40 Length 00:04:48""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 18"" - cell ""Artist Skizologic Clear"": - text: Artist - textbox: Skizologic @@ -1025,14 +926,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:48"": - text: Length - textbox: 00:04:48 - - row ""# Increment Decrement Artist Thomas Schumacher, Caitlin Clear Title All of You (Remix) Clear Begin 01:36:40 End 01:42:16 Length 00:05:36"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""19"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 19 Artist Thomas Schumacher, Caitlin Clear Title All of You (Remix) Clear Begin 01:36:40 End 01:42:16 Length 00:05:36""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 19"" - cell ""Artist Thomas Schumacher, Caitlin Clear"": - text: Artist - textbox: Thomas Schumacher, Caitlin @@ -1052,14 +950,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:36"": - text: Length - textbox: 00:05:36 - - row ""# Increment Decrement Artist A. Skomoroh Clear Title White Horse Conquest (Original Mix) Clear Begin 01:42:16 End 01:47:04 Length 00:04:48"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""20"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 20 Artist A. Skomoroh Clear Title White Horse Conquest (Original Mix) Clear Begin 01:42:16 End 01:47:04 Length 00:04:48""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 20"" - cell ""Artist A. Skomoroh Clear"": - text: Artist - textbox: A. Skomoroh @@ -1079,14 +974,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:48"": - text: Length - textbox: 00:04:48 - - row ""# Increment Decrement Artist Patrik Berg Clear Title Bright (Original Mix) Clear Begin 01:47:04 End 01:52:37 Length 00:05:33"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""21"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 21 Artist Patrik Berg Clear Title Bright (Original Mix) Clear Begin 01:47:04 End 01:52:37 Length 00:05:33""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 21"" - cell ""Artist Patrik Berg Clear"": - text: Artist - textbox: Patrik Berg @@ -1106,14 +998,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:33"": - text: Length - textbox: 00:05:33 - - row ""# Increment Decrement Artist Hidden Empire Clear Title Bengal Clear Begin 01:52:37 End 01:58:05 Length 00:05:28"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""22"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 22 Artist Hidden Empire Clear Title Bengal Clear Begin 01:52:37 End 01:58:05 Length 00:05:28""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 22"" - cell ""Artist Hidden Empire Clear"": - text: Artist - textbox: Hidden Empire @@ -1133,14 +1022,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:28"": - text: Length - textbox: 00:05:28 - - row ""# Increment Decrement Artist Mario Ochoa Clear Title Levitate Clear Begin 01:58:05 End 02:03:00 Length 00:04:55"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""23"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 23 Artist Mario Ochoa Clear Title Levitate Clear Begin 01:58:05 End 02:03:00 Length 00:04:55""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 23"" - cell ""Artist Mario Ochoa Clear"": - text: Artist - textbox: Mario Ochoa @@ -1160,14 +1046,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:55"": - text: Length - textbox: 00:04:55 - - row ""# Increment Decrement Artist Raul Facio Clear Title Eyes Wide Shut (Original Mix) Clear Begin 02:03:00 End 02:08:21 Length 00:05:21"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""24"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 24 Artist Raul Facio Clear Title Eyes Wide Shut (Original Mix) Clear Begin 02:03:00 End 02:08:21 Length 00:05:21""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 24"" - cell ""Artist Raul Facio Clear"": - text: Artist - textbox: Raul Facio @@ -1187,14 +1070,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:21"": - text: Length - textbox: 00:05:21 - - row ""# Increment Decrement Artist Soolver Clear Title Regular (Original Mix) Clear Begin 02:08:21 End 02:14:31 Length 00:06:10"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""25"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 25 Artist Soolver Clear Title Regular (Original Mix) Clear Begin 02:08:21 End 02:14:31 Length 00:06:10""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 25"" - cell ""Artist Soolver Clear"": - text: Artist - textbox: Soolver @@ -1214,14 +1094,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:06:10"": - text: Length - textbox: 00:06:10 - - row ""# Increment Decrement Artist Weska Clear Title EQ64 (Original Mix) Clear Begin 02:14:31 End 02:18:35 Length 00:04:04"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""26"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 26 Artist Weska Clear Title EQ64 (Original Mix) Clear Begin 02:14:31 End 02:18:35 Length 00:04:04""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 26"" - cell ""Artist Weska Clear"": - text: Artist - textbox: Weska @@ -1241,14 +1118,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:04"": - text: Length - textbox: 00:04:04 - - row ""# Increment Decrement Artist Tempo Giusto Clear Title The Fall (Extended Mix) Clear Begin 02:18:35 End 02:24:12 Length 00:05:37"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""27"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 27 Artist Tempo Giusto Clear Title The Fall (Extended Mix) Clear Begin 02:18:35 End 02:24:12 Length 00:05:37""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 27"" - cell ""Artist Tempo Giusto Clear"": - text: Artist - textbox: Tempo Giusto @@ -1268,14 +1142,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:37"": - text: Length - textbox: 00:05:37 - - row ""# Increment Decrement Artist Vlind & Asteroid & Gary Leroy Clear Title Trinity (Extended Mix) Clear Begin 02:24:12 End 02:29:38 Length 00:05:26"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""28"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 28 Artist Vlind & Asteroid & Gary Leroy Clear Title Trinity (Extended Mix) Clear Begin 02:24:12 End 02:29:38 Length 00:05:26""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 28"" - cell ""Artist Vlind & Asteroid & Gary Leroy Clear"": - text: Artist - textbox: Vlind & Asteroid & Gary Leroy @@ -1295,14 +1166,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:26"": - text: Length - textbox: 00:05:26 - - row ""# Increment Decrement Artist Astral Legacy Clear Title Vaveyla (Original Mix) Clear Begin 02:29:38 End 02:32:52 Length 00:03:14"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""29"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 29 Artist Astral Legacy Clear Title Vaveyla (Original Mix) Clear Begin 02:29:38 End 02:32:52 Length 00:03:14""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 29"" - cell ""Artist Astral Legacy Clear"": - text: Artist - textbox: Astral Legacy @@ -1322,14 +1190,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:03:14"": - text: Length - textbox: 00:03:14 - - row ""# Increment Decrement Artist Gerrox Clear Title Chakra (Original Mix) Clear Begin 02:32:52 End 02:37:00 Length 00:04:08"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""30"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 30 Artist Gerrox Clear Title Chakra (Original Mix) Clear Begin 02:32:52 End 02:37:00 Length 00:04:08""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 30"" - cell ""Artist Gerrox Clear"": - text: Artist - textbox: Gerrox @@ -1349,14 +1214,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:08"": - text: Length - textbox: 00:04:08 - - row ""# Increment Decrement Artist Charlotte De Witte Clear Title Pattern Clear Begin 02:37:00 End 02:41:55 Length 00:04:55"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""31"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 31 Artist Charlotte De Witte Clear Title Pattern Clear Begin 02:37:00 End 02:41:55 Length 00:04:55""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 31"" - cell ""Artist Charlotte De Witte Clear"": - text: Artist - textbox: Charlotte De Witte @@ -1376,14 +1238,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:55"": - text: Length - textbox: 00:04:55 - - row ""# Increment Decrement Artist Space Food Clear Title Amabey Clear Begin 02:41:55 End 02:46:55 Length 00:05:00"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""32"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 32 Artist Space Food Clear Title Amabey Clear Begin 02:41:55 End 02:46:55 Length 00:05:00""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 32"" - cell ""Artist Space Food Clear"": - text: Artist - textbox: Space Food @@ -1403,14 +1262,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:00"": - text: Length - textbox: 00:05:00 - - row ""# Increment Decrement Artist ARTBAT Clear Title Papilion (Original Mix) Clear Begin 02:46:55 End 02:51:13 Length 00:04:18"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""33"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 33 Artist ARTBAT Clear Title Papilion (Original Mix) Clear Begin 02:46:55 End 02:51:13 Length 00:04:18""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 33"" - cell ""Artist ARTBAT Clear"": - text: Artist - textbox: ARTBAT @@ -1430,14 +1286,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:18"": - text: Length - textbox: 00:04:18 - - row ""# Increment Decrement Artist PETER PAHN Clear Title Enjoy Infinity (Original Mix) Clear Begin 02:51:13 End 02:56:08 Length 00:04:55"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""34"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 34 Artist PETER PAHN Clear Title Enjoy Infinity (Original Mix) Clear Begin 02:51:13 End 02:56:08 Length 00:04:55""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 34"" - cell ""Artist PETER PAHN Clear"": - text: Artist - textbox: PETER PAHN @@ -1457,14 +1310,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:55"": - text: Length - textbox: 00:04:55 - - row ""# Increment Decrement Artist Solitek Clear Title Instinct (Original Mix) Clear Begin 02:56:08 End 03:00:57 Length 00:04:49"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""35"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 35 Artist Solitek Clear Title Instinct (Original Mix) Clear Begin 02:56:08 End 03:00:57 Length 00:04:49""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 35"" - cell ""Artist Solitek Clear"": - text: Artist - textbox: Solitek @@ -1484,14 +1334,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:49"": - text: Length - textbox: 00:04:49 - - row ""# Increment Decrement Artist Veerus Clear Title Heavy Clear Begin 03:00:57 End 03:05:19 Length 00:04:22"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""36"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 36 Artist Veerus Clear Title Heavy Clear Begin 03:00:57 End 03:05:19 Length 00:04:22""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 36"" - cell ""Artist Veerus Clear"": - text: Artist - textbox: Veerus @@ -1511,14 +1358,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:22"": - text: Length - textbox: 00:04:22 - - row ""# Increment Decrement Artist Secret Cinema & Reinier Zonneveld Clear Title Pain Thing (Original Mix) Clear Begin 03:05:19 End 03:09:38 Length 00:04:19"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""37"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 37 Artist Secret Cinema & Reinier Zonneveld Clear Title Pain Thing (Original Mix) Clear Begin 03:05:19 End 03:09:38 Length 00:04:19""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 37"" - cell ""Artist Secret Cinema & Reinier Zonneveld Clear"": - text: Artist - textbox: Secret Cinema & Reinier Zonneveld @@ -1538,14 +1382,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:19"": - text: Length - textbox: 00:04:19 - - row ""# Increment Decrement Artist Amelie Lens Clear Title Hypnotized Clear Begin 03:09:38 End 03:13:13 Length 00:03:35"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""38"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 38 Artist Amelie Lens Clear Title Hypnotized Clear Begin 03:09:38 End 03:13:13 Length 00:03:35""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 38"" - cell ""Artist Amelie Lens Clear"": - text: Artist - textbox: Amelie Lens @@ -1565,14 +1406,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:03:35"": - text: Length - textbox: 00:03:35 - - row ""# Increment Decrement Artist Nikolay Kirov Clear Title Chasing the Sun (Original Mix) Clear Begin 03:13:13 End Length"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""39"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 39 Artist Nikolay Kirov Clear Title Chasing the Sun (Original Mix) Clear Begin 03:13:13 End Length""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 39"" - cell ""Artist Nikolay Kirov Clear"": - text: Artist - textbox: Nikolay Kirov @@ -1605,15 +1443,13 @@ public async Task Import_ShouldImportTracks_WhenUsingSampleInputfile2() await Expect(importView.CuesheetArtistInput).ToBeEmptyAsync(); await Expect(importView.CuesheetTitleInput).ToBeEmptyAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Sample Artist 1 Clear"": - text: Artist - textbox: Sample Artist 1 @@ -1633,14 +1469,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:05:00"": - text: Length - textbox: 00:05:00 - - row ""# Increment Decrement Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Sample Artist 2 Clear"": - text: Artist - textbox: Sample Artist 2 @@ -1660,14 +1493,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:23"": - text: Length - textbox: 00:04:23 - - row ""# Increment Decrement Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Sample Artist 3 Clear"": - text: Artist - textbox: Sample Artist 3 @@ -1687,14 +1517,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:06:31"": - text: Length - textbox: 00:06:31 - - row ""# Increment Decrement Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Sample Artist 4 Clear"": - text: Artist - textbox: Sample Artist 4 @@ -1714,14 +1541,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:19"": - text: Length - textbox: 00:04:19 - - row ""# Increment Decrement Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Sample Artist 5 Clear"": - text: Artist - textbox: Sample Artist 5 @@ -1741,14 +1565,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:04:41"": - text: Length - textbox: 00:04:41 - - row ""# Increment Decrement Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Sample Artist 6 Clear"": - text: Artist - textbox: Sample Artist 6 @@ -1768,14 +1589,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:07:00"": - text: Length - textbox: 00:07:00 - - row ""# Increment Decrement Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sample Artist 7 Clear"": - text: Artist - textbox: Sample Artist 7 @@ -1795,14 +1613,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Length 00:14:00"": - text: Length - textbox: 00:14:00 - - row ""# Increment Decrement Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Sample Artist 8 Clear"": - text: Artist - textbox: Sample Artist 8 @@ -1834,15 +1649,13 @@ public async Task Import_ShouldImportTracks_WithTraktorOutput() await importView.Analyze(); await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Nachap Clear Title Glass Clear Begin 00:00:00 End 00:11:56 Length 00:11:56 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Nachap Clear Title Glass Clear Begin 00:00:00 End 00:11:56 Length 00:11:56 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Nachap Clear"": - text: Artist - textbox: Nachap @@ -1863,14 +1676,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Length - textbox: 00:11:56 - cell ""Status"" - - row ""# Increment Decrement Artist Title Progressive + Melodic DECK Clear Begin 00:11:56 End 00:13:31 Length 00:01:35 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Title Progressive + Melodic DECK Clear Begin 00:11:56 End 00:13:31 Length 00:01:35 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist"": - text: Artist - textbox @@ -1892,14 +1702,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Nomer 21 Clear Title Depersonalization Clear Begin 00:13:31 End 00:19:14 Length 00:05:43 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Nomer 21 Clear Title Depersonalization Clear Begin 00:13:31 End 00:19:14 Length 00:05:43 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Nomer 21 Clear"": - text: Artist - textbox: Nomer 21 @@ -1922,14 +1729,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Title Showing Off Clear Begin 00:19:14 End 00:24:02 Length 00:04:48 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist SevenEver, Nopopstar, 2JOHN''S & Eugene Jay Clear Title Showing Off Clear Begin 00:19:14 End 00:24:02 Length 00:04:48 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear"": - text: Artist - textbox: SevenEver, Nopopstar, 2JOHN'S & Eugene Jay @@ -1952,14 +1756,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Carlo Whale Clear Title Unconscious Clear Begin 00:24:02 End 00:29:37 Length 00:05:35 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Carlo Whale Clear Title Unconscious Clear Begin 00:24:02 End 00:29:37 Length 00:05:35 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Carlo Whale Clear"": - text: Artist - textbox: Carlo Whale @@ -1982,14 +1783,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Arba Han Clear Title Timelaps Clear Begin 00:29:37 End 00:33:13 Length 00:03:36 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Arba Han Clear Title Timelaps Clear Begin 00:29:37 End 00:33:13 Length 00:03:36 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Arba Han Clear"": - text: Artist - textbox: Arba Han @@ -2012,14 +1810,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Savill Clear Title Energy Surrounds Clear Begin 00:33:13 End 00:40:30 Length 00:07:17 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Savill Clear Title Energy Surrounds Clear Begin 00:33:13 End 00:40:30 Length 00:07:17 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Savill Clear"": - text: Artist - textbox: Savill @@ -2042,14 +1837,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Teklix Clear Title The Tribal Code Clear Begin 00:40:30 End 00:48:53 Length 00:08:23 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Teklix Clear Title The Tribal Code Clear Begin 00:40:30 End 00:48:53 Length 00:08:23 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Teklix Clear"": - text: Artist - textbox: Teklix @@ -2072,14 +1864,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Neuralis Clear Title I'm Looking for Answers Clear Begin 00:48:53 End 00:54:34 Length 00:05:41 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""9"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 9 Artist Neuralis Clear Title I''m Looking for Answers Clear Begin 00:48:53 End 00:54:34 Length 00:05:41 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 9"" - cell ""Artist Neuralis Clear"": - text: Artist - textbox: Neuralis @@ -2102,14 +1891,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Nopopstar & Arsia Clear Title Dirty Moves Clear Begin 00:54:34 End 01:00:24 Length 00:05:50 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""10"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 10 Artist Nopopstar & Arsia Clear Title Dirty Moves Clear Begin 00:54:34 End 01:00:24 Length 00:05:50 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 10"" - cell ""Artist Nopopstar & Arsia Clear"": - text: Artist - textbox: Nopopstar & Arsia @@ -2132,14 +1918,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Title Lost (Maze 28 Remix) Clear Begin 01:00:24 End 01:05:17 Length 00:04:53 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""11"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 11 Artist SevenEver, Nopopstar, 2JOHN''S & Eugene Jay Clear Title Lost (Maze 28 Remix) Clear Begin 01:00:24 End 01:05:17 Length 00:04:53 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 11"" - cell ""Artist SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear"": - text: Artist - textbox: SevenEver, Nopopstar, 2JOHN'S & Eugene Jay @@ -2162,14 +1945,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear Title Lost (Redspace Remix) Clear Begin 01:05:17 End 01:08:22 Length 00:03:05 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""12"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 12 Artist SevenEver, Nopopstar, 2JOHN''S & Eugene Jay Clear Title Lost (Redspace Remix) Clear Begin 01:05:17 End 01:08:22 Length 00:03:05 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 12"" - cell ""Artist SevenEver, Nopopstar, 2JOHN'S & Eugene Jay Clear"": - text: Artist - textbox: SevenEver, Nopopstar, 2JOHN'S & Eugene Jay @@ -2192,14 +1972,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Gadoz Clear Title 5d Clear Begin 01:08:22 End 01:16:34 Length 00:08:12 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""13"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 13 Artist Gadoz Clear Title 5d Clear Begin 01:08:22 End 01:16:34 Length 00:08:12 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 13"" - cell ""Artist Gadoz Clear"": - text: Artist - textbox: Gadoz @@ -2222,14 +1999,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist DJ Danzik Clear Title Out of Space Clear Begin 01:16:34 End 01:19:22 Length 00:02:48 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""14"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 14 Artist DJ Danzik Clear Title Out of Space Clear Begin 01:16:34 End 01:19:22 Length 00:02:48 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 14"" - cell ""Artist DJ Danzik Clear"": - text: Artist - textbox: DJ Danzik @@ -2252,14 +2026,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Enis Çoban Clear Title Internet Clear Begin 01:19:22 End 01:25:14 Length 00:05:52 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""15"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 15 Artist Enis Çoban Clear Title Internet Clear Begin 01:19:22 End 01:25:14 Length 00:05:52 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 15"" - cell ""Artist Enis Çoban Clear"": - text: Artist - textbox: Enis Çoban @@ -2282,14 +2053,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Cold Mind & Alex Yikker Clear Title Rage Clear Begin 01:25:14 End 01:31:33 Length 00:06:19 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""16"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 16 Artist Cold Mind & Alex Yikker Clear Title Rage Clear Begin 01:25:14 End 01:31:33 Length 00:06:19 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 16"" - cell ""Artist Cold Mind & Alex Yikker Clear"": - text: Artist - textbox: Cold Mind & Alex Yikker @@ -2312,14 +2080,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Maze 28 Clear Title Sol (JAHAYA Remix) Clear Begin 01:31:33 End 01:32:51 Length 00:01:18 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""17"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 17 Artist Maze 28 Clear Title Sol (JAHAYA Remix) Clear Begin 01:31:33 End 01:32:51 Length 00:01:18 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 17"" - cell ""Artist Maze 28 Clear"": - text: Artist - textbox: Maze 28 @@ -2342,14 +2107,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Alex Grafton Clear Title Hi Baby Clear Begin 01:32:51 End 01:42:13 Length 00:09:22 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""18"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 18 Artist Alex Grafton Clear Title Hi Baby Clear Begin 01:32:51 End 01:42:13 Length 00:09:22 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 18"" - cell ""Artist Alex Grafton Clear"": - text: Artist - textbox: Alex Grafton @@ -2372,14 +2134,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Che&Mos & Halo Far Clear Title Daddy Clear Begin 01:42:13 End 01:45:22 Length 00:03:09 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""19"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 19 Artist Che&Mos & Halo Far Clear Title Daddy Clear Begin 01:42:13 End 01:45:22 Length 00:03:09 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 19"" - cell ""Artist Che&Mos & Halo Far Clear"": - text: Artist - textbox: Che&Mos & Halo Far @@ -2402,14 +2161,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist K KARDEN Clear Title Acid Rain Clear Begin 01:45:22 End 01:48:40 Length 00:03:18 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""20"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 20 Artist K KARDEN Clear Title Acid Rain Clear Begin 01:45:22 End 01:48:40 Length 00:03:18 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 20"" - cell ""Artist K KARDEN Clear"": - text: Artist - textbox: K KARDEN @@ -2432,14 +2188,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Dobrov & Gar1sson Clear Title Analogic (Redspace Remix) Clear Begin 01:48:40 End 01:56:40 Length 00:08:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""21"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 21 Artist Dobrov & Gar1sson Clear Title Analogic (Redspace Remix) Clear Begin 01:48:40 End 01:56:40 Length 00:08:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 21"" - cell ""Artist Dobrov & Gar1sson Clear"": - text: Artist - textbox: Dobrov & Gar1sson @@ -2462,14 +2215,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Gazfluz Clear Title Vargan Clear Begin 01:56:40 End 02:00:39 Length 00:03:59 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""22"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 22 Artist Gazfluz Clear Title Vargan Clear Begin 01:56:40 End 02:00:39 Length 00:03:59 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 22"" - cell ""Artist Gazfluz Clear"": - text: Artist - textbox: Gazfluz @@ -2492,14 +2242,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist SHKAPOV Clear Title Control Clear Begin 02:00:39 End 02:06:47 Length 00:06:08 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""23"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 23 Artist SHKAPOV Clear Title Control Clear Begin 02:00:39 End 02:06:47 Length 00:06:08 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 23"" - cell ""Artist SHKAPOV Clear"": - text: Artist - textbox: SHKAPOV @@ -2522,14 +2269,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist QazaQ Clear Title On the Line Clear Begin 02:06:47 End 02:09:09 Length 00:02:22 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""24"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 24 Artist QazaQ Clear Title On the Line Clear Begin 02:06:47 End 02:09:09 Length 00:02:22 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 24"" - cell ""Artist QazaQ Clear"": - text: Artist - textbox: QazaQ @@ -2552,14 +2296,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Alex Schaufel Clear Title Elizabeth (Larsun Hesh Remix) Clear Begin 02:09:09 End 02:14:18 Length 00:05:09 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""25"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 25 Artist Alex Schaufel Clear Title Elizabeth (Larsun Hesh Remix) Clear Begin 02:09:09 End 02:14:18 Length 00:05:09 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 25"" - cell ""Artist Alex Schaufel Clear"": - text: Artist - textbox: Alex Schaufel @@ -2582,14 +2323,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Oiro Clear Title Just Business Clear Begin 02:14:18 End 02:19:22 Length 00:05:04 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""26"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 26 Artist Oiro Clear Title Just Business Clear Begin 02:14:18 End 02:19:22 Length 00:05:04 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 26"" - cell ""Artist Oiro Clear"": - text: Artist - textbox: Oiro @@ -2612,14 +2350,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Molex Clear Title Mind Split (Redspace Remix) Clear Begin 02:19:22 End 02:27:48 Length 00:08:26 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""27"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 27 Artist Molex Clear Title Mind Split (Redspace Remix) Clear Begin 02:19:22 End 02:27:48 Length 00:08:26 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 27"" - cell ""Artist Molex Clear"": - text: Artist - textbox: Molex @@ -2642,14 +2377,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist SOLI Clear Title Give Me Your Mind Clear Begin 02:27:48 End 02:33:48 Length 00:06:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""28"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 28 Artist SOLI Clear Title Give Me Your Mind Clear Begin 02:27:48 End 02:33:48 Length 00:06:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 28"" - cell ""Artist SOLI Clear"": - text: Artist - textbox: SOLI @@ -2672,14 +2404,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist MANDU Clear Title Jacky Clear Begin 02:33:48 End 02:39:05 Length 00:05:17 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""29"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 29 Artist MANDU Clear Title Jacky Clear Begin 02:33:48 End 02:39:05 Length 00:05:17 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 29"" - cell ""Artist MANDU Clear"": - text: Artist - textbox: MANDU @@ -2702,14 +2431,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist SOLI Clear Title Spacetoon Clear Begin 02:39:05 End 02:41:31 Length 00:02:26 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""30"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 30 Artist SOLI Clear Title Spacetoon Clear Begin 02:39:05 End 02:41:31 Length 00:02:26 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 30"" - cell ""Artist SOLI Clear"": - text: Artist - textbox: SOLI @@ -2732,14 +2458,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sasha Fashion Clear Title Moqton Clear Begin 02:41:31 End 02:50:18 Length 00:08:47 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""31"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 31 Artist Sasha Fashion Clear Title Moqton Clear Begin 02:41:31 End 02:50:18 Length 00:08:47 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 31"" - cell ""Artist Sasha Fashion Clear"": - text: Artist - textbox: Sasha Fashion @@ -2762,14 +2485,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist NAASA Clear Title Poison Clear Begin 02:50:18 End 02:56:45 Length 00:06:27 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""32"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 32 Artist NAASA Clear Title Poison Clear Begin 02:50:18 End 02:56:45 Length 00:06:27 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 32"" - cell ""Artist NAASA Clear"": - text: Artist - textbox: NAASA @@ -2792,14 +2512,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Nopopstar, 2JOHN'S & Eugene Jay Clear Title Nightlong Clear Begin 02:56:45 End 02:59:14 Length 00:02:29 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""33"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 33 Artist Nopopstar, 2JOHN''S & Eugene Jay Clear Title Nightlong Clear Begin 02:56:45 End 02:59:14 Length 00:02:29 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 33"" - cell ""Artist Nopopstar, 2JOHN'S & Eugene Jay Clear"": - text: Artist - textbox: Nopopstar, 2JOHN'S & Eugene Jay @@ -2822,14 +2539,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Skillz jay Clear Title Choir Clear Begin 02:59:14 End 03:05:49 Length 00:06:35 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""34"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 34 Artist Skillz jay Clear Title Choir Clear Begin 02:59:14 End 03:05:49 Length 00:06:35 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 34"" - cell ""Artist Skillz jay Clear"": - text: Artist - textbox: Skillz jay @@ -2852,14 +2566,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Kovax Clear Title Controller Clear Begin 03:05:49 End 03:08:31 Length 00:02:42 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""35"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 35 Artist Kovax Clear Title Controller Clear Begin 03:05:49 End 03:08:31 Length 00:02:42 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 35"" - cell ""Artist Kovax Clear"": - text: Artist - textbox: Kovax @@ -2882,14 +2593,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Mumboi Clear Title Just a Beat Clear Begin 03:08:31 End 03:15:18 Length 00:06:47 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""36"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 36 Artist Mumboi Clear Title Just a Beat Clear Begin 03:08:31 End 03:15:18 Length 00:06:47 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 36"" - cell ""Artist Mumboi Clear"": - text: Artist - textbox: Mumboi @@ -2912,14 +2620,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Eclept Clear Title Sprut Clear Begin 03:15:18 End 03:20:35 Length 00:05:17 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""37"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 37 Artist Eclept Clear Title Sprut Clear Begin 03:15:18 End 03:20:35 Length 00:05:17 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 37"" - cell ""Artist Eclept Clear"": - text: Artist - textbox: Eclept @@ -2942,14 +2647,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Rudensky Clear Title Dark Escort Clear Begin 03:20:35 End 03:25:02 Length 00:04:27 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""38"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 38 Artist Rudensky Clear Title Dark Escort Clear Begin 03:20:35 End 03:25:02 Length 00:04:27 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 38"" - cell ""Artist Rudensky Clear"": - text: Artist - textbox: Rudensky @@ -2972,14 +2674,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Alexey Union, Kinky Sound & KOCHETOV Clear Title Connected Clear Begin 03:25:02 End 03:32:30 Length 00:07:28 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""39"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 39 Artist Alexey Union, Kinky Sound & KOCHETOV Clear Title Connected Clear Begin 03:25:02 End 03:32:30 Length 00:07:28 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 39"" - cell ""Artist Alexey Union, Kinky Sound & KOCHETOV Clear"": - text: Artist - textbox: Alexey Union, Kinky Sound & KOCHETOV @@ -3002,14 +2701,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist ANMA (MD) Clear Title Space Yoda (Snyl Remix) Clear Begin 03:32:30 End 03:37:53 Length 00:05:23 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""40"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 40 Artist ANMA (MD) Clear Title Space Yoda (Snyl Remix) Clear Begin 03:32:30 End 03:37:53 Length 00:05:23 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 40"" - cell ""Artist ANMA (MD) Clear"": - text: Artist - textbox: ANMA (MD) @@ -3032,14 +2728,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Inache Clear Title Andale (MONTA (TN) Remix) Clear Begin 03:37:53 End Length Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""41"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 41 Artist Inache Clear Title Andale (MONTA (TN) Remix) Clear Begin 03:37:53 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 41"" - cell ""Artist Inache Clear"": - text: Artist - textbox: Inache @@ -3074,15 +2767,13 @@ public async Task Open_ShouldImportProject_WhenUsingProjectfile() await Expect(detailView.CuesheetArtistInput).ToHaveValueAsync("Sample CD Artist"); await Expect(detailView.CuesheetTitleInput).ToHaveValueAsync("Sample CD Title"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Sample Artist 1 Clear"": - text: Artist - textbox: Sample Artist 1 @@ -3103,14 +2794,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Length - textbox: 00:05:00 - cell ""Status"" - - row ""# Increment Decrement Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Sample Artist 2 Clear"": - text: Artist - textbox: Sample Artist 2 @@ -3133,14 +2821,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Sample Artist 3 Clear"": - text: Artist - textbox: Sample Artist 3 @@ -3163,14 +2848,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Sample Artist 4 Clear"": - text: Artist - textbox: Sample Artist 4 @@ -3193,14 +2875,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Sample Artist 5 Clear"": - text: Artist - textbox: Sample Artist 5 @@ -3223,14 +2902,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Sample Artist 6 Clear"": - text: Artist - textbox: Sample Artist 6 @@ -3253,14 +2929,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sample Artist 7 Clear"": - text: Artist - textbox: Sample Artist 7 @@ -3283,14 +2956,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End Length Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Sample Artist 8 Clear"": - text: Artist - textbox: Sample Artist 8 @@ -3327,15 +2997,13 @@ public async Task Open_ShouldImportCuesheet_WhenUsingCuesheetfile() await Expect(detailView.CuesheetArtistInput).ToHaveValueAsync("Sample CD Artist"); await Expect(detailView.CuesheetTitleInput).ToHaveValueAsync("Sample CD Title"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Sample Artist 1 Clear"": - text: Artist - textbox: Sample Artist 1 @@ -3356,14 +3024,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Length - textbox: 00:05:00 - cell ""Status"" - - row ""# Increment Decrement Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Sample Artist 2 Clear"": - text: Artist - textbox: Sample Artist 2 @@ -3386,14 +3051,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Sample Artist 3 Clear"": - text: Artist - textbox: Sample Artist 3 @@ -3416,14 +3078,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Sample Artist 4 Clear"": - text: Artist - textbox: Sample Artist 4 @@ -3446,14 +3105,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Sample Artist 5 Clear"": - text: Artist - textbox: Sample Artist 5 @@ -3476,14 +3132,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Sample Artist 6 Clear"": - text: Artist - textbox: Sample Artist 6 @@ -3506,14 +3159,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sample Artist 7 Clear"": - text: Artist - textbox: Sample Artist 7 @@ -3536,14 +3186,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End Length Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Sample Artist 8 Clear"": - text: Artist - textbox: Sample Artist 8 @@ -3580,15 +3227,13 @@ public async Task Import_ShouldImportTracks_WhenUsingText() await importView.Analyze(); await importView.CompleteImportAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Sample Artist 1 Clear Title Sample Title 1 Clear Begin 00:00:00 End 00:05:00 Length 00:05:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Sample Artist 1 Clear"": - text: Artist - textbox: Sample Artist 1 @@ -3609,14 +3254,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Length - textbox: 00:05:00 - cell ""Status"" - - row ""# Increment Decrement Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Sample Artist 2 Clear Title Sample Title 2 Clear Begin 00:05:00 End 00:09:23 Length 00:04:23 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Sample Artist 2 Clear"": - text: Artist - textbox: Sample Artist 2 @@ -3639,14 +3281,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Sample Artist 3 Clear Title Sample Title 3 Clear Begin 00:09:23 End 00:15:54 Length 00:06:31 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Sample Artist 3 Clear"": - text: Artist - textbox: Sample Artist 3 @@ -3669,14 +3308,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Sample Artist 4 Clear Title Sample Title 4 Clear Begin 00:15:54 End 00:20:13 Length 00:04:19 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Sample Artist 4 Clear"": - text: Artist - textbox: Sample Artist 4 @@ -3699,14 +3335,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Sample Artist 5 Clear Title Sample Title 5 Clear Begin 00:20:13 End 00:24:54 Length 00:04:41 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Sample Artist 5 Clear"": - text: Artist - textbox: Sample Artist 5 @@ -3729,14 +3362,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Sample Artist 6 Clear Title Sample Title 6 Clear Begin 00:24:54 End 00:31:54 Length 00:07:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Sample Artist 6 Clear"": - text: Artist - textbox: Sample Artist 6 @@ -3759,14 +3389,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sample Artist 7 Clear Title Sample Title 7 Clear Begin 00:31:54 End 00:45:54 Length 00:14:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sample Artist 7 Clear"": - text: Artist - textbox: Sample Artist 7 @@ -3789,14 +3416,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Sample Artist 8 Clear Title Sample Title 8 Clear Begin 00:45:54 End 01:15:54 Length 00:30:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Sample Artist 8 Clear"": - text: Artist - textbox: Sample Artist 8 diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs index 66a61e4a..1174a2ce 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs @@ -40,7 +40,7 @@ public async Task Record_ShouldRecordTracks_WhenTracksAdded() await viewModes.SwitchView("Detail view"); await detailView.SelectTracksAsync([1]); await detailView.EditSelectedTracksModalAsync(); - await Expect(TestPage.GetByRole(AriaRole.Checkbox, new() { Name = "Link to previous track" })).ToBeCheckedAsync(); + await Expect(TestPage.GetByRole(AriaRole.Switch, new() { Name = "Link to previous track" })).ToBeCheckedAsync(); } } } diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/TracingTestSmartphone.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/TracingTestSmartphone.cs index e0198f87..d5ac1b48 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/TracingTestSmartphone.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/TracingTestSmartphone.cs @@ -39,22 +39,42 @@ public async Task UndoRedo_ShouldRestoreTrackState_WhenUndoAndRedoAreUsed() await bar.UndoAsync(); await bar.UndoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row: - - cell"); + - 'row ""Select row # 1 Artist Title Begin 00:00:00 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" + - cell ""Artist"": + - text: Artist + - textbox + - button + - cell ""Title"": + - text: Title + - textbox + - button + - cell ""Begin 00:00:00"": + - text: Begin + - textbox: 00:00:00 + - cell ""End"": + - text: End + - textbox + - cell ""Length"": + - text: Length + - textbox + - cell ""Status"""); await Expect(bar.RedoButton).ToBeEnabledAsync(); await bar.RedoAsync(); await bar.RedoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Test Artist 1 Clear Title Test Title 1 Clear Begin 00:00:00 End Length Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Test Artist 1 Clear Title Test Title 1 Clear Begin 00:00:00 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Test Artist 1 Clear"": - text: Artist - textbox: Test Artist 1 @@ -77,15 +97,13 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"""); await detailView.EditTrackAsync("Mozart", "Eine kleine Nachtmusik"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Mozart Clear Title Eine kleine Nachtmusik Clear Begin 00:00:00 End Length Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Mozart Clear Title Eine kleine Nachtmusik Clear Begin 00:00:00 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Mozart Clear"": - text: Artist - textbox: Mozart @@ -108,15 +126,13 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"""); await bar.UndoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Mozart Clear Title Test Title 1 Clear Begin 00:00:00 End Length Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Mozart Clear Title Test Title 1 Clear Begin 00:00:00 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Mozart Clear"": - text: Artist - textbox: Mozart @@ -149,15 +165,13 @@ public async Task UndoRedo_ShouldRestoreTrackState_WhenModalEdit() await detailView.SelectTracksAsync([1]); await detailView.EditTracksModalAsync("Test Track Artist 1", "Test Track Title 1", "00:02:23", ["channel audio (4CH)", "Serial copy management system"]); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Test Track Artist 1 Clear Title Test Track Title 1 Clear Begin 00:00:00 End 00:02:23 Length 00:02:23 Status"": - - cell: - - checkbox [checked] - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Test Track Artist 1 Clear Title Test Track Title 1 Clear Begin 00:00:00 End 00:02:23 Length 00:02:23 Status"" [selected]': + - cell ""Select row"": + - checkbox ""Select row"" [checked] + - text: Select row + - cell ""# 1"" - cell ""Artist Test Track Artist 1 Clear"": - text: Artist - textbox: Test Track Artist 1 @@ -180,15 +194,13 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"""); await bar.UndoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Title Begin 00:00:00 End Length Status"": - - cell: - - checkbox [checked] - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Title Begin 00:00:00 End End has no value! Length Length has no value! Status"" [selected]': + - cell ""Select row"": + - checkbox ""Select row"" [checked] + - text: Select row + - cell ""# 1"" - cell ""Artist"": - text: Artist - textbox @@ -200,24 +212,24 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Begin 00:00:00"": - text: Begin - textbox: 00:00:00 - - cell ""End"": + - cell ""End End has no value!"": - text: End - textbox - - cell ""Length"": + - text: End has no value! + - cell ""Length Length has no value!"": - text: Length - textbox + - text: Length has no value! - cell ""Status"""); await bar.RedoAsync(); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Test Track Artist 1 Clear Title Test Track Title 1 Clear Begin 00:00:00 End 00:02:23 Length 00:02:23 Status"": - - cell: - - checkbox [checked] - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Test Track Artist 1 Clear Title Test Track Title 1 Clear Begin 00:00:00 End 00:02:23 Length 00:02:23 Status"" [selected]': + - cell ""Select row"": + - checkbox ""Select row"" [checked] + - text: Select row + - cell ""# 1"" - cell ""Artist Test Track Artist 1 Clear"": - text: Artist - textbox: Test Track Artist 1 @@ -263,15 +275,13 @@ public async Task UndoRedo_ShouldRestoreCuesheet_WhenUsingImport() await Expect(importView.CuesheetTitleInput).ToHaveValueAsync("Rabbit Hole Mix"); await Expect(importView.CatalogueNumberInput).ToHaveValueAsync("01 23456 78912 3"); await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup - rowgroup: - - row ""# Increment Decrement Artist Adriatique Clear Title X. Clear Begin 00:00:00 End 00:05:24.2500000 Length 00:05:24.2500000 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""1"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 1 Artist Adriatique Clear Title X. Clear Begin 00:00:00 End 00:05:24.2500000 Length 00:05:24.2500000 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" - cell ""Artist Adriatique Clear"": - text: Artist - textbox: Adriatique @@ -292,14 +302,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Length - textbox: 00:05:24.2500000 - cell ""Status"" - - row ""# Increment Decrement Artist Third Harmony Clear Title Fears And Dreams (Original Mix) Clear Begin 00:05:24.2500000 End 00:10:39 Length 00:05:14.7500000 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""2"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 2 Artist Third Harmony Clear Title Fears And Dreams (Original Mix) Clear Begin 00:05:24.2500000 End 00:10:39 Length 00:05:14.7500000 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" - cell ""Artist Third Harmony Clear"": - text: Artist - textbox: Third Harmony @@ -322,14 +329,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Dele Sosimi Afrobeat Orchestra Clear Title Too Much Information (Laolu Remix; Edit) Clear Begin 00:10:39 End 00:17:06 Length 00:06:27 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""3"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 3 Artist Dele Sosimi Afrobeat Orchestra Clear Title Too Much Information (Laolu Remix; Edit) Clear Begin 00:10:39 End 00:17:06 Length 00:06:27 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 3"" - cell ""Artist Dele Sosimi Afrobeat Orchestra Clear"": - text: Artist - textbox: Dele Sosimi Afrobeat Orchestra @@ -352,14 +356,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Edem, Govan Clear Title Ankh (Onetwo MX Remix) Clear Begin 00:17:06 End 00:23:21 Length 00:06:15 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""4"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 4 Artist Edem, Govan Clear Title Ankh (Onetwo MX Remix) Clear Begin 00:17:06 End 00:23:21 Length 00:06:15 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 4"" - cell ""Artist Edem, Govan Clear"": - text: Artist - textbox: Edem, Govan @@ -382,14 +383,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Jody Wisternoff Clear Title For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear Begin 00:23:21 End 00:29:02 Length 00:05:41 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""5"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 5 Artist Jody Wisternoff Clear Title For All Time (feat. Hendrik Burkhard) (Extended Mix) Clear Begin 00:23:21 End 00:29:02 Length 00:05:41 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 5"" - cell ""Artist Jody Wisternoff Clear"": - text: Artist - textbox: Jody Wisternoff @@ -412,14 +410,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Einmusik Clear Title Bead (Original Mix) Clear Begin 00:29:02 End 00:34:27 Length 00:05:25 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""6"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 6 Artist Einmusik Clear Title Bead (Original Mix) Clear Begin 00:29:02 End 00:34:27 Length 00:05:25 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 6"" - cell ""Artist Einmusik Clear"": - text: Artist - textbox: Einmusik @@ -442,14 +437,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Sebastien Leger Clear Title La Danse du Scorpion Clear Begin 00:34:27 End 00:40:59 Length 00:06:32 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""7"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 7 Artist Sebastien Leger Clear Title La Danse du Scorpion Clear Begin 00:34:27 End 00:40:59 Length 00:06:32 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 7"" - cell ""Artist Sebastien Leger Clear"": - text: Artist - textbox: Sebastien Leger @@ -472,14 +464,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Paul Thomas & Solid Stone Clear Title La Bombo (Solid Stone Remix) Clear Begin 00:40:59 End 00:46:19 Length 00:05:20 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""8"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 8 Artist Paul Thomas & Solid Stone Clear Title La Bombo (Solid Stone Remix) Clear Begin 00:40:59 End 00:46:19 Length 00:05:20 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 8"" - cell ""Artist Paul Thomas & Solid Stone Clear"": - text: Artist - textbox: Paul Thomas & Solid Stone @@ -502,14 +491,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist GusGus Clear Title Crossfade (Maceo Plex Mix) Clear Begin 00:46:19 End 00:52:20 Length 00:06:01 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""9"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 9 Artist GusGus Clear Title Crossfade (Maceo Plex Mix) Clear Begin 00:46:19 End 00:52:20 Length 00:06:01 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 9"" - cell ""Artist GusGus Clear"": - text: Artist - textbox: GusGus @@ -532,14 +518,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Klangkarussell Clear Title Time (Original Mix) Clear Begin 00:52:20 End 00:56:19 Length 00:03:59 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""10"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 10 Artist Klangkarussell Clear Title Time (Original Mix) Clear Begin 00:52:20 End 00:56:19 Length 00:03:59 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 10"" - cell ""Artist Klangkarussell Clear"": - text: Artist - textbox: Klangkarussell @@ -562,14 +545,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Anysense & Un:said Clear Title Missing Path (Original Mix) Clear Begin 00:56:19 End 01:01:41 Length 00:05:22 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""11"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 11 Artist Anysense & Un:said Clear Title Missing Path (Original Mix) Clear Begin 00:56:19 End 01:01:41 Length 00:05:22 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 11"" - cell ""Artist Anysense & Un:said Clear"": - text: Artist - textbox: Anysense & Un:said @@ -592,14 +572,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Space Food Clear Title Bombay Clear Begin 01:01:41 End 01:06:33 Length 00:04:52 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""12"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 12 Artist Space Food Clear Title Bombay Clear Begin 01:01:41 End 01:06:33 Length 00:04:52 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 12"" - cell ""Artist Space Food Clear"": - text: Artist - textbox: Space Food @@ -622,14 +599,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist SHDW & Obscure Shape Clear Title Wächter der Nacht (Original Mix) Clear Begin 01:06:33 End 01:11:04 Length 00:04:31 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""13"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 13 Artist SHDW & Obscure Shape Clear Title Wächter der Nacht (Original Mix) Clear Begin 01:06:33 End 01:11:04 Length 00:04:31 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 13"" - cell ""Artist SHDW & Obscure Shape Clear"": - text: Artist - textbox: SHDW & Obscure Shape @@ -652,14 +626,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist HOSH Clear Title Karma Clear Begin 01:11:04 End 01:15:28 Length 00:04:24 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""14"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 14 Artist HOSH Clear Title Karma Clear Begin 01:11:04 End 01:15:28 Length 00:04:24 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 14"" - cell ""Artist HOSH Clear"": - text: Artist - textbox: HOSH @@ -682,14 +653,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Alexey Union Clear Title Olympia (Original Mix) Clear Begin 01:15:28 End 01:21:08 Length 00:05:40 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""15"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 15 Artist Alexey Union Clear Title Olympia (Original Mix) Clear Begin 01:15:28 End 01:21:08 Length 00:05:40 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 15"" - cell ""Artist Alexey Union Clear"": - text: Artist - textbox: Alexey Union @@ -712,14 +680,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Paul Taylor Clear Title Afterglow Clear Begin 01:21:08 End 01:25:38 Length 00:04:30 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""16"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 16 Artist Paul Taylor Clear Title Afterglow Clear Begin 01:21:08 End 01:25:38 Length 00:04:30 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 16"" - cell ""Artist Paul Taylor Clear"": - text: Artist - textbox: Paul Taylor @@ -742,14 +707,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Philter Clear Title Stranger Clear Begin 01:25:38 End 01:31:52 Length 00:06:14 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""17"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 17 Artist Philter Clear Title Stranger Clear Begin 01:25:38 End 01:31:52 Length 00:06:14 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 17"" - cell ""Artist Philter Clear"": - text: Artist - textbox: Philter @@ -772,14 +734,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Skizologic Clear Title Hypersphere (Original Mix) Clear Begin 01:31:52 End 01:36:40 Length 00:04:48 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""18"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 18 Artist Skizologic Clear Title Hypersphere (Original Mix) Clear Begin 01:31:52 End 01:36:40 Length 00:04:48 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 18"" - cell ""Artist Skizologic Clear"": - text: Artist - textbox: Skizologic @@ -802,14 +761,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Thomas Schumacher, Caitlin Clear Title All of You (Remix) Clear Begin 01:36:40 End 01:42:16 Length 00:05:36 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""19"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 19 Artist Thomas Schumacher, Caitlin Clear Title All of You (Remix) Clear Begin 01:36:40 End 01:42:16 Length 00:05:36 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 19"" - cell ""Artist Thomas Schumacher, Caitlin Clear"": - text: Artist - textbox: Thomas Schumacher, Caitlin @@ -832,14 +788,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist A. Skomoroh Clear Title White Horse Conquest (Original Mix) Clear Begin 01:42:16 End 01:47:04 Length 00:04:48 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""20"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 20 Artist A. Skomoroh Clear Title White Horse Conquest (Original Mix) Clear Begin 01:42:16 End 01:47:04 Length 00:04:48 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 20"" - cell ""Artist A. Skomoroh Clear"": - text: Artist - textbox: A. Skomoroh @@ -862,14 +815,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Patrik Berg Clear Title Bright (Original Mix) Clear Begin 01:47:04 End 01:52:37 Length 00:05:33 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""21"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 21 Artist Patrik Berg Clear Title Bright (Original Mix) Clear Begin 01:47:04 End 01:52:37 Length 00:05:33 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 21"" - cell ""Artist Patrik Berg Clear"": - text: Artist - textbox: Patrik Berg @@ -892,14 +842,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Hidden Empire Clear Title Bengal Clear Begin 01:52:37 End 01:58:05 Length 00:05:28 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""22"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 22 Artist Hidden Empire Clear Title Bengal Clear Begin 01:52:37 End 01:58:05 Length 00:05:28 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 22"" - cell ""Artist Hidden Empire Clear"": - text: Artist - textbox: Hidden Empire @@ -922,14 +869,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Mario Ochoa Clear Title Levitate Clear Begin 01:58:05 End 02:03:00 Length 00:04:55 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""23"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 23 Artist Mario Ochoa Clear Title Levitate Clear Begin 01:58:05 End 02:03:00 Length 00:04:55 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 23"" - cell ""Artist Mario Ochoa Clear"": - text: Artist - textbox: Mario Ochoa @@ -952,14 +896,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Raul Facio Clear Title Eyes Wide Shut (Original Mix) Clear Begin 02:03:00 End 02:08:21 Length 00:05:21 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""24"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 24 Artist Raul Facio Clear Title Eyes Wide Shut (Original Mix) Clear Begin 02:03:00 End 02:08:21 Length 00:05:21 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 24"" - cell ""Artist Raul Facio Clear"": - text: Artist - textbox: Raul Facio @@ -982,14 +923,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Soolver Clear Title Regular (Original Mix) Clear Begin 02:08:21 End 02:14:31 Length 00:06:10 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""25"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 25 Artist Soolver Clear Title Regular (Original Mix) Clear Begin 02:08:21 End 02:14:31 Length 00:06:10 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 25"" - cell ""Artist Soolver Clear"": - text: Artist - textbox: Soolver @@ -1012,14 +950,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Weska Clear Title EQ64 (Original Mix) Clear Begin 02:14:31 End 02:18:35 Length 00:04:04 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""26"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 26 Artist Weska Clear Title EQ64 (Original Mix) Clear Begin 02:14:31 End 02:18:35 Length 00:04:04 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 26"" - cell ""Artist Weska Clear"": - text: Artist - textbox: Weska @@ -1042,14 +977,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Tempo Giusto Clear Title The Fall (Extended Mix) Clear Begin 02:18:35 End 02:24:12 Length 00:05:37 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""27"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 27 Artist Tempo Giusto Clear Title The Fall (Extended Mix) Clear Begin 02:18:35 End 02:24:12 Length 00:05:37 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 27"" - cell ""Artist Tempo Giusto Clear"": - text: Artist - textbox: Tempo Giusto @@ -1072,14 +1004,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Vlind & Asteroid & Gary Leroy Clear Title Trinity (Extended Mix) Clear Begin 02:24:12 End 02:29:38 Length 00:05:26 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""28"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 28 Artist Vlind & Asteroid & Gary Leroy Clear Title Trinity (Extended Mix) Clear Begin 02:24:12 End 02:29:38 Length 00:05:26 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 28"" - cell ""Artist Vlind & Asteroid & Gary Leroy Clear"": - text: Artist - textbox: Vlind & Asteroid & Gary Leroy @@ -1102,14 +1031,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Astral Legacy Clear Title Vaveyla (Original Mix) Clear Begin 02:29:38 End 02:32:52 Length 00:03:14 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""29"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 29 Artist Astral Legacy Clear Title Vaveyla (Original Mix) Clear Begin 02:29:38 End 02:32:52 Length 00:03:14 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 29"" - cell ""Artist Astral Legacy Clear"": - text: Artist - textbox: Astral Legacy @@ -1132,14 +1058,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Gerrox Clear Title Chakra (Original Mix) Clear Begin 02:32:52 End 02:37:00 Length 00:04:08 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""30"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 30 Artist Gerrox Clear Title Chakra (Original Mix) Clear Begin 02:32:52 End 02:37:00 Length 00:04:08 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 30"" - cell ""Artist Gerrox Clear"": - text: Artist - textbox: Gerrox @@ -1162,14 +1085,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Charlotte De Witte Clear Title Pattern Clear Begin 02:37:00 End 02:41:55 Length 00:04:55 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""31"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 31 Artist Charlotte De Witte Clear Title Pattern Clear Begin 02:37:00 End 02:41:55 Length 00:04:55 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 31"" - cell ""Artist Charlotte De Witte Clear"": - text: Artist - textbox: Charlotte De Witte @@ -1192,14 +1112,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Space Food Clear Title Amabey Clear Begin 02:41:55 End 02:46:55 Length 00:05:00 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""32"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 32 Artist Space Food Clear Title Amabey Clear Begin 02:41:55 End 02:46:55 Length 00:05:00 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 32"" - cell ""Artist Space Food Clear"": - text: Artist - textbox: Space Food @@ -1222,14 +1139,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist ARTBAT Clear Title Papilion (Original Mix) Clear Begin 02:46:55 End 02:51:13 Length 00:04:18 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""33"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 33 Artist ARTBAT Clear Title Papilion (Original Mix) Clear Begin 02:46:55 End 02:51:13 Length 00:04:18 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 33"" - cell ""Artist ARTBAT Clear"": - text: Artist - textbox: ARTBAT @@ -1252,14 +1166,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist PETER PAHN Clear Title Enjoy Infinity (Original Mix) Clear Begin 02:51:13 End 02:56:08 Length 00:04:55 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""34"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 34 Artist PETER PAHN Clear Title Enjoy Infinity (Original Mix) Clear Begin 02:51:13 End 02:56:08 Length 00:04:55 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 34"" - cell ""Artist PETER PAHN Clear"": - text: Artist - textbox: PETER PAHN @@ -1282,14 +1193,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Solitek Clear Title Instinct (Original Mix) Clear Begin 02:56:08 End 03:00:57 Length 00:04:49 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""35"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 35 Artist Solitek Clear Title Instinct (Original Mix) Clear Begin 02:56:08 End 03:00:57 Length 00:04:49 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 35"" - cell ""Artist Solitek Clear"": - text: Artist - textbox: Solitek @@ -1312,14 +1220,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Veerus Clear Title Heavy Clear Begin 03:00:57 End 03:05:19 Length 00:04:22 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""36"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 36 Artist Veerus Clear Title Heavy Clear Begin 03:00:57 End 03:05:19 Length 00:04:22 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 36"" - cell ""Artist Veerus Clear"": - text: Artist - textbox: Veerus @@ -1342,14 +1247,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Secret Cinema & Reinier Zonneveld Clear Title Pain Thing (Original Mix) Clear Begin 03:05:19 End 03:09:38 Length 00:04:19 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""37"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 37 Artist Secret Cinema & Reinier Zonneveld Clear Title Pain Thing (Original Mix) Clear Begin 03:05:19 End 03:09:38 Length 00:04:19 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 37"" - cell ""Artist Secret Cinema & Reinier Zonneveld Clear"": - text: Artist - textbox: Secret Cinema & Reinier Zonneveld @@ -1372,14 +1274,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Amelie Lens Clear Title Hypnotized Clear Begin 03:09:38 End 03:13:13 Length 00:03:35 Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""38"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 38 Artist Amelie Lens Clear Title Hypnotized Clear Begin 03:09:38 End 03:13:13 Length 00:03:35 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 38"" - cell ""Artist Amelie Lens Clear"": - text: Artist - textbox: Amelie Lens @@ -1402,14 +1301,11 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - cell ""Status"": - text: Status - button - - row ""# Increment Decrement Artist Nikolay Kirov Clear Title Chasing the Sun (Original Mix) Clear Begin 03:13:13 End Length Status"": - - cell: - - checkbox - - cell ""# Increment Decrement"": - - text: ""#"" - - spinbutton: ""39"" - - button ""Increment"" - - button ""Decrement"" + - 'row ""Select row # 39 Artist Nikolay Kirov Clear Title Chasing the Sun (Original Mix) Clear Begin 03:13:13 End Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 39"" - cell ""Artist Nikolay Kirov Clear"": - text: Artist - textbox: Nikolay Kirov @@ -1433,5 +1329,100 @@ await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- ta - text: Status - button"); } + + [TestMethod] + public async Task UndoRedo_ShouldRestoreTrackState_WhenUndoWithAudiofile() + { + var bar = new AppBar(TestPage); + var detailView = new DetailView(TestPage); + await detailView.GotoAsync(); + await detailView.AddTrackAsync(); + await detailView.AudiofileInput.SetInputFilesAsync("Kalimba.mp3"); + await detailView.AddTrackAsync(); + await Expect(bar.UndoButton).ToBeEnabledAsync(); + await Expect(bar.RedoButton).ToBeDisabledAsync(); + await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup + - rowgroup: + - 'row ""Select row # 1 Artist Title Begin 00:00:00 End End has no value! Length Length has no value! Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" + - cell ""Artist"": + - text: Artist + - textbox + - button + - cell ""Title"": + - text: Title + - textbox + - button + - cell ""Begin 00:00:00"": + - text: Begin + - textbox: 00:00:00 + - cell ""End End has no value!"": + - text: End + - textbox + - text: End has no value! + - cell ""Length Length has no value!"": + - text: Length + - textbox + - text: Length has no value! + - cell ""Status"" + - 'row ""Select row # 2 Artist Title Begin End 00:05:48.0608330 Length Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 2"" + - cell ""Artist"": + - text: Artist + - textbox + - button + - cell ""Title"": + - text: Title + - textbox + - button + - cell ""Begin"": + - text: Begin + - textbox + - cell ""End 00:05:48.0608330"": + - text: End + - textbox: 00:05:48.0608330 + - cell ""Length"": + - text: Length + - textbox + - cell ""Status"": + - text: Status + - button"); + await bar.UndoAsync(); + await Expect(bar.UndoButton).ToBeEnabledAsync(); + await Expect(bar.RedoButton).ToBeEnabledAsync(); + await Expect(TestPage.GetByRole(AriaRole.Table)).ToMatchAriaSnapshotAsync(@"- table: + - rowgroup + - rowgroup: + - 'row ""Select row # 1 Artist Title Begin 00:00:00 End 00:05:48.0608330 Length 00:05:48.0608330 Status""': + - cell ""Select row"": + - checkbox ""Select row"" + - text: Select row + - cell ""# 1"" + - cell ""Artist"": + - text: Artist + - textbox + - button + - cell ""Title"": + - text: Title + - textbox + - button + - cell ""Begin 00:00:00"": + - text: Begin + - textbox: 00:00:00 + - cell ""End 00:05:48.0608330"": + - text: End + - textbox: 00:05:48.0608330 + - cell ""Length 00:05:48.0608330"": + - text: Length + - textbox: 00:05:48.0608330 + - cell ""Status"""); + } } } diff --git a/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj b/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj index 0651db7f..79308649 100644 --- a/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj +++ b/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj @@ -12,11 +12,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs b/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs index 0db99667..6487ec33 100644 --- a/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs +++ b/AudioCuesheetEditor.Tests/Model/AudioCuesheet/CuesheetTests.cs @@ -15,733 +15,147 @@ //. using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.Entity; -using AudioCuesheetEditor.Model.IO.Audio; -using AudioCuesheetEditor.Tests.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading; namespace AudioCuesheetEditor.Tests.Model.AudioCuesheet { [TestClass()] public class CuesheetTests { - [TestMethod()] - public void AddTrackTest() - { - var cuesheet = new Cuesheet(); - Assert.IsEmpty(cuesheet.Tracks); - cuesheet.AddTrack(new Track()); - Assert.HasCount(1, cuesheet.Tracks); - } - - [TestMethod()] - public void CuesheetTest() - { - var cuesheet = new Cuesheet(); - Assert.IsNull(cuesheet.Audiofile); - var validationErrorAudioFile = cuesheet.Validate(nameof(Cuesheet.Audiofile)); - Assert.AreEqual(ValidationStatus.Error, validationErrorAudioFile.Status); - cuesheet.Audiofile = new Audiofile("AudioFile01.ogg"); - validationErrorAudioFile = cuesheet.Validate(nameof(Cuesheet.Audiofile)); - Assert.AreEqual(ValidationStatus.Success, validationErrorAudioFile.Status); - } - - [TestMethod()] - public void EmptyCuesheetTracksValidationTest() - { - var cuesheet = new Cuesheet(); - Assert.IsEmpty(cuesheet.Tracks); - var validationErrorTracks = cuesheet.Validate(nameof(Cuesheet.Tracks)); - Assert.AreEqual(ValidationStatus.Error, validationErrorTracks.Status); - cuesheet.AddTrack(new Track()); - validationErrorTracks = cuesheet.Validate(nameof(Cuesheet.Tracks)); - Assert.AreEqual(ValidationStatus.Success, validationErrorTracks.Status); - } - - [TestMethod()] - public void RecordTest() - { - var cuesheet = new Cuesheet(); - Assert.IsFalse(cuesheet.IsRecording); - Assert.IsNull(cuesheet.RecordingStart); - cuesheet.StartRecording(); - Assert.IsTrue(cuesheet.IsRecording); - Assert.IsNotNull(cuesheet.RecordingStart); - var track = new Track(); - Assert.IsNull(track.Begin); - Assert.IsNull(track.End); - cuesheet.AddTrack(track); - Assert.AreEqual(TimeSpan.Zero, track.Begin); - Assert.IsNull(track.End); - var track2 = new Track(); - cuesheet.AddTrack(track2); - Assert.IsNotNull(track.End); - Assert.AreNotEqual(TimeSpan.Zero, track.End); - cuesheet = new Cuesheet(); - cuesheet.StartRecording(); - track = new Track(); - cuesheet.AddTrack(track); - Assert.AreEqual(TimeSpan.Zero, track.Begin); - Assert.IsNull(track.End); - Thread.Sleep(3000); - track2 = new Track(); - cuesheet.AddTrack(track2); - Assert.IsNotNull(track.End); - Assert.AreNotEqual(TimeSpan.Zero, track.End); - cuesheet.StopRecording(); - Assert.IsNotNull(track.End); - Assert.AreEqual(track.End, track2.Begin); - } - - [TestMethod()] - public void TrackRecalculationTest() - { - var cuesheet = new Cuesheet(); - var track1 = new Track(); - var track2 = new Track(); - var track3 = new Track(); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - Assert.AreEqual((uint)1, track1.Position); - Assert.AreEqual((uint)2, track2.Position); - Assert.AreEqual((uint)3, track3.Position); - Assert.AreEqual(track1.Begin, TimeSpan.Zero); - Assert.IsNull(track1.End); - Assert.IsNull(track2.Begin); - Assert.IsNull(track2.End); - Assert.IsNull(track3.Begin); - Assert.IsNull(track3.End); - track1.Begin = TimeSpan.Zero; - track2.Begin = new TimeSpan(0, 2, 43); - Assert.IsNull(track1.End); - Assert.IsNull(track2.End); - track3.End = new TimeSpan(0, 12, 14); - Assert.IsNull(track2.End); - Assert.IsNull(track3.Begin); - track3.Begin = new TimeSpan(0, 7, 56); - Assert.IsNull(track2.End); - } - [TestMethod()] - public void TrackOverlappingTest() - { - var testHelper = new TestHelper(); - var cuesheet = new Cuesheet(); - var track1 = new Track(); - var track2 = new Track(); - var track3 = new Track(); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - Assert.AreEqual((uint)1, track1.Position); - Assert.AreEqual((uint)2, track2.Position); - Assert.AreEqual((uint)3, track3.Position); - var validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); - Assert.AreEqual(ValidationStatus.Success, validationResult.Status); - track1.Position = 1; - track2.Position = 1; - track3.Position = 1; - track1.End = new TimeSpan(0, 2, 30); - track2.Begin = new TimeSpan(0, 2, 0); - track2.End = new TimeSpan(0, 5, 30); - track3.Begin = new TimeSpan(0, 4, 54); - track3.End = new TimeSpan(0, 8, 12); - validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); - Assert.AreEqual(ValidationStatus.Error, validationResult.Status); - Assert.AreEqual(7, validationResult.ValidationMessages?.Count); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0} {1} '{2}' is used also by {3}({4},{5},{6},{7},{8}). Positions must be unique!" && x.Parameter != null && x.Parameter[2].Equals(track1.Position) && x.Parameter[7].Equals(track1.Begin) && x.Parameter[8].Equals(track1.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0} {1} '{2}' is used also by {3}({4},{5},{6},{7},{8}). Positions must be unique!" && x.Parameter != null && x.Parameter[2].Equals(track2.Position) && x.Parameter[7].Equals(track2.Begin) && x.Parameter[8].Equals(track2.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0} {1} '{2}' is used also by {3}({4},{5},{6},{7},{8}). Positions must be unique!" && x.Parameter != null && x.Parameter[2].Equals(track3.Position) && x.Parameter[7].Equals(track3.Begin) && x.Parameter[8].Equals(track3.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track1.Position) && x.Parameter[4].Equals(track1.Begin) && x.Parameter[5].Equals(track1.End) && x.Parameter[6].Equals(track2.Position) && x.Parameter[9].Equals(track2.Begin) && x.Parameter[10].Equals(track2.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track2.Position) && x.Parameter[4].Equals(track2.Begin) && x.Parameter[5].Equals(track2.End) && x.Parameter[6].Equals(track1.Position) && x.Parameter[9].Equals(track1.Begin) && x.Parameter[10].Equals(track1.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track2.Position) && x.Parameter[4].Equals(track2.Begin) && x.Parameter[5].Equals(track2.End) && x.Parameter[6].Equals(track3.Position) && x.Parameter[9].Equals(track3.Begin) && x.Parameter[10].Equals(track3.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track3.Position) && x.Parameter[4].Equals(track3.Begin) && x.Parameter[5].Equals(track3.End) && x.Parameter[6].Equals(track2.Position) && x.Parameter[9].Equals(track2.Begin) && x.Parameter[10].Equals(track2.End))); - track2.End = new TimeSpan(0, 5, 15); - validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); - Assert.AreEqual(ValidationStatus.Error, validationResult.Status); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track2.Position) && x.Parameter[4].Equals(track2.Begin) && x.Parameter[5].Equals(track2.End) && x.Parameter[6].Equals(track3.Position) && x.Parameter[9].Equals(track3.Begin) && x.Parameter[10].Equals(track3.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track3.Position) && x.Parameter[4].Equals(track3.Begin) && x.Parameter[5].Equals(track3.End) && x.Parameter[6].Equals(track2.Position) && x.Parameter[9].Equals(track2.Begin) && x.Parameter[10].Equals(track2.End))); - track1.Position = 1; - track2.Position = 2; - track3.Position = 3; - validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); - Assert.AreEqual(ValidationStatus.Error, validationResult.Status); - Assert.AreEqual(4, validationResult.ValidationMessages?.Count); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track1.Position) && x.Parameter[4].Equals(track1.Begin) && x.Parameter[5].Equals(track1.End) && x.Parameter[6].Equals(track2.Position) && x.Parameter[9].Equals(track2.Begin) && x.Parameter[10].Equals(track2.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track2.Position) && x.Parameter[4].Equals(track2.Begin) && x.Parameter[5].Equals(track2.End) && x.Parameter[6].Equals(track1.Position) && x.Parameter[9].Equals(track1.Begin) && x.Parameter[10].Equals(track1.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track2.Position) && x.Parameter[4].Equals(track2.Begin) && x.Parameter[5].Equals(track2.End) && x.Parameter[6].Equals(track3.Position) && x.Parameter[9].Equals(track3.Begin) && x.Parameter[10].Equals(track3.End))); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!" && x.Parameter != null && x.Parameter[1].Equals(track3.Position) && x.Parameter[4].Equals(track3.Begin) && x.Parameter[5].Equals(track3.End) && x.Parameter[6].Equals(track2.Position) && x.Parameter[9].Equals(track2.Begin) && x.Parameter[10].Equals(track2.End))); - track2.Begin = new TimeSpan(0, 2, 30); - track3.Begin = new TimeSpan(0, 5, 15); - validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); - Assert.AreEqual(ValidationStatus.Success, validationResult.Status); - } - - [TestMethod()] - public void RemoveTrack_ShouldReCalculate_WithPreviouslyAddedTracks() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track() { Artist = "1", Title = "1", IsLinkedToPreviousTrack = true }; - var track2 = new Track() { Artist = "2", Title = "2", IsLinkedToPreviousTrack = true }; - var track3 = new Track() { Artist = "3", Title = "3" , IsLinkedToPreviousTrack = true }; - var track4 = new Track() { Artist = "4", Title = "4" , IsLinkedToPreviousTrack = true }; - var track5 = new Track() { Artist = "5", Title = "5" , IsLinkedToPreviousTrack = true }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - cuesheet.AddTrack(track4); - cuesheet.AddTrack(track5); - track1.End = new TimeSpan(0, 5, 0); - track2.End = new TimeSpan(0, 10, 0); - track3.End = new TimeSpan(0, 15, 0); - track4.End = new TimeSpan(0, 20, 0); - track5.End = new TimeSpan(0, 25, 0); - // Act - cuesheet.RemoveTrack(track2); - // Assert - Assert.AreEqual((uint)2, track3.Position); - Assert.AreEqual((uint)3, track4.Position); - Assert.AreEqual((uint)4, track5.Position); - } - - [TestMethod()] - public void RemoveTracks_ShouldRemoveTracks_WithPreviouslyAddedTracks() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track - { - Artist = "Track 1", - Title = "Track 1", - IsLinkedToPreviousTrack = true - }; - cuesheet.AddTrack(track1); - var track2 = new Track - { - Title = "Track 2", - Artist = "Track 2", - Begin = new TimeSpan(0, 5, 0), - IsLinkedToPreviousTrack = true - }; - cuesheet.AddTrack(track2); - var track3 = new Track - { - Artist = "Track 3", - Title = "Track 3", - Begin = new TimeSpan(0, 10, 0), - IsLinkedToPreviousTrack = true - }; - cuesheet.AddTrack(track3); - var track4 = new Track - { - Artist = "Track 4", - Title = "Track 4", - Begin = new TimeSpan(0, 15, 0), - IsLinkedToPreviousTrack = true - }; - cuesheet.AddTrack(track4); - var track5 = new Track - { - Artist = "Track 5", - Title = "Track 5", - Begin = new TimeSpan(0, 20, 0), IsLinkedToPreviousTrack = true - }; - cuesheet.AddTrack(track5); - var list = new List() { track2, track4 }; - // Act - cuesheet.RemoveTracks(list.AsReadOnly()); - // Assert - Assert.HasCount(3, cuesheet.Tracks); - Assert.AreEqual(new TimeSpan(0, 5, 0), track3.Begin); - Assert.AreEqual(new TimeSpan(0, 15, 0), track5.Begin); - Assert.AreEqual((uint)1, track1.Position); - Assert.AreEqual((uint)2, track3.Position); - Assert.AreEqual((uint)3, track5.Position); - } - - [TestMethod()] - public void UnlickedTrackTest() - { - var cuesheet = new Cuesheet(); - var track1 = new Track - { - Position = 1, - End = new TimeSpan(0, 3, 23) - }; - var track2 = new Track - { - Position = 2 - }; - var track3 = new Track - { - Position = 3 - }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - Assert.AreEqual(track1.End, track2.Begin); - Assert.IsNull(track2.End); - Assert.IsNull(track3.Begin); - track3.Begin = new TimeSpan(0, 7, 32); - Assert.IsNull(track2.End); - } - - [TestMethod()] - public void TrackPositionChangedTest() - { - var cuesheet = new Cuesheet(); - var track1 = new Track - { - Position = 3, - End = new TimeSpan(0, 5, 0) - }; - var track2 = new Track - { - Position = 2, - Begin = track1.End, - End = new TimeSpan(0, 7, 30) - }; - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track1); - Assert.AreEqual(track2, cuesheet.Tracks.First()); - Assert.AreEqual(track1, cuesheet.Tracks.Last()); - track1.Position = 1; - Assert.AreEqual(track2, cuesheet.Tracks.First()); - Assert.AreEqual(track1, cuesheet.Tracks.Last()); - } - - [TestMethod()] - public void TrackLengthChangedWithIsLinkedToPreivousTest() - { - var cuesheet = new Cuesheet(); - var track1 = new Track(); - var track2 = new Track(); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - track1.IsLinkedToPreviousTrack = true; - track2.IsLinkedToPreviousTrack = true; - Assert.IsNull(track2.Begin); - Assert.IsNotNull(track1.Begin); - var editedTrack = new Track() { Begin = TimeSpan.Zero, Length = new TimeSpan(0, 2, 23) }; - track1.CopyValues(editedTrack); - Assert.IsNotNull(track1.End); - Assert.AreEqual(track1.End, track2.Begin); - Assert.AreEqual(editedTrack.End, track2.Begin); - } - - [TestMethod()] - public void ValidateTest() - { - var cuesheet = new Cuesheet(); - Assert.AreEqual(ValidationStatus.Error, cuesheet.Validate(nameof(Cuesheet.Artist)).Status); - cuesheet.Artist = "Testartist"; - Assert.AreEqual(ValidationStatus.Success, cuesheet.Validate(nameof(Cuesheet.Artist)).Status); - Assert.AreEqual(ValidationStatus.Error, cuesheet.Validate(nameof(Cuesheet.Title)).Status); - cuesheet.Title = "Testtitle"; - Assert.AreEqual(ValidationStatus.Success, cuesheet.Validate(nameof(Cuesheet.Title)).Status); - } - - [TestMethod()] - public void IsLinkedToPreviousTrack_ChangedLastTrackBegin_SetsTrackProperties() - { - //Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track() - { - Artist = "Track1 Artist", - Title = "Track1 Title" - }; - var track2 = new Track() - { - Artist = "Track2 Artist", - Title = "Track2 Title", - End = new TimeSpan(0, 9, 12) - }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - track2.Begin = new TimeSpan(0, 4, 23); - //Act - track2.IsLinkedToPreviousTrack = true; - //Assert - Assert.AreEqual((uint)2, track2.Position); - Assert.AreEqual(track2.Begin, track1.End); - } - - [TestMethod()] - public void IsRecordingPossible_WhenRecordingIsAlreadyRunning_ReturnsError() - { - // Arrange - var cuesheet = new Cuesheet(); - cuesheet.StartRecording(); - - // Act - var errors = cuesheet.IsRecordingPossible.ToList(); - - // Assert - Assert.Contains("Record is already running!", errors); - } - - [TestMethod()] - public void IsRecordingPossible_WhenCuesheetContainsTracks_ReturnsError() - { - // Arrange - var cuesheet = new Cuesheet(); - cuesheet.AddTrack(new Track()); - - // Act - var errors = cuesheet.IsRecordingPossible.ToList(); - - // Assert - Assert.Contains("Cuesheet already contains tracks!", errors); - } - - [TestMethod()] - public void IsRecordingPossible_WhenNoErrors_ReturnsEmpty() - { - // Arrange - var cuesheet = new Cuesheet(); - - // Act - var errors = cuesheet.IsRecordingPossible.ToList(); - - // Assert - Assert.IsEmpty(errors); - } - - [TestMethod] - public void MoveTracksPossible_ShouldReturnFalse_WhenNoTracksToMove() - { - // Arrange - var cuesheet = new Cuesheet(); - var tracksToMove = new List(); - - // Act - var result = cuesheet.MoveTracksPossible(tracksToMove, MoveDirection.Up); - - // Assert - Assert.IsFalse(result); - } - - [TestMethod] - public void MoveTracksPossible_ShouldReturnFalse_WhenMovingUpAndFirstTrack() - { - // Arrange - var cuesheet = new Cuesheet(); - var track = new Track(); - cuesheet.AddTrack(track); - var tracksToMove = new List { track }; - - // Act - var result = cuesheet.MoveTracksPossible(tracksToMove, MoveDirection.Up); - - // Assert - Assert.IsFalse(result); - } - - [TestMethod] - public void MoveTracksPossible_ShouldReturnTrue_WhenMovingUpAndNotFirstTrack() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track(); - var track2 = new Track(); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - var tracksToMove = new List { track2 }; - - // Act - var result = cuesheet.MoveTracksPossible(tracksToMove, MoveDirection.Up); - - // Assert - Assert.IsTrue(result); - } - - [TestMethod] - public void MoveTracksPossible_ShouldReturnFalse_WhenMovingDownAndLastTrack() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track(); - var track2 = new Track(); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - var tracksToMove = new List { track2 }; - - // Act - var result = cuesheet.MoveTracksPossible(tracksToMove, MoveDirection.Down); - - // Assert - Assert.IsFalse(result); - } - - - [TestMethod] - public void MoveTracksPossible_ShouldReturnTrue_WhenMovingDownAndNotLastTrack() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track(); - var track2 = new Track(); - var track3 = new Track(); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - var tracksToMove = new List { track2 }; - - // Act - var result = cuesheet.MoveTracksPossible(tracksToMove, MoveDirection.Down); - - // Assert - Assert.IsTrue(result); - } - - [TestMethod] - public void MoveTracks_Up_Success() + public void Validate_AudiofileNull_ReturnsValidationStatusError() { // Arrange var cuesheet = new Cuesheet(); - var track1 = new Track { Position = 1 }; - var track2 = new Track { Position = 2 }; - var track3 = new Track { Position = 3 }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - // Act - cuesheet.MoveTracks([track2], MoveDirection.Up); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Audiofile)); // Assert - Assert.AreEqual((uint)2, cuesheet.Tracks.ElementAt(0).Position); - Assert.AreEqual((uint)1, cuesheet.Tracks.ElementAt(1).Position); - Assert.AreEqual((uint)3, cuesheet.Tracks.ElementAt(2).Position); - Assert.AreEqual(track2, cuesheet.Tracks.ElementAt(0)); - Assert.AreEqual(track1, cuesheet.Tracks.ElementAt(1)); - Assert.AreEqual(track3, cuesheet.Tracks.ElementAt(2)); - } - - [TestMethod] - public void MoveTracks_Down_Success() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track { Position = 1 }; - var track2 = new Track { Position = 2 }; - var track3 = new Track { Position = 3 }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - - // Act - cuesheet.MoveTracks([track2], MoveDirection.Down); - - // Assert - Assert.AreEqual((uint)1, cuesheet.Tracks.ElementAt(0).Position); - Assert.AreEqual((uint)3, cuesheet.Tracks.ElementAt(1).Position); - Assert.AreEqual((uint)2, cuesheet.Tracks.ElementAt(2).Position); - Assert.AreEqual(track1, cuesheet.Tracks.ElementAt(0)); - Assert.AreEqual(track3, cuesheet.Tracks.ElementAt(1)); - Assert.AreEqual(track2, cuesheet.Tracks.ElementAt(2)); - } - - [TestMethod] - public void MoveTracks_Up_NotPossible() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track { Position = 1 }; - var track2 = new Track { Position = 2 }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - - // Act - cuesheet.MoveTracks([track1], MoveDirection.Up); - - // Assert - Assert.AreEqual((uint)1, cuesheet.Tracks.ElementAt(0).Position); - Assert.AreEqual((uint)2, cuesheet.Tracks.ElementAt(1).Position); - Assert.AreEqual(track1, cuesheet.Tracks.ElementAt(0)); - Assert.AreEqual(track2, cuesheet.Tracks.ElementAt(1)); - } - - [TestMethod] - public void MoveTracks_Down_NotPossible() - { - // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track { Position = 1 }; - var track2 = new Track { Position = 2 }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - - // Act - cuesheet.MoveTracks([track2], MoveDirection.Down); - - // Assert - Assert.AreEqual((uint)1, cuesheet.Tracks.ElementAt(0).Position); - Assert.AreEqual((uint)2, cuesheet.Tracks.ElementAt(1).Position); - Assert.AreEqual(track1, cuesheet.Tracks.ElementAt(0)); - Assert.AreEqual(track2, cuesheet.Tracks.ElementAt(1)); - } - - [TestMethod] - public void StartRecording_WithValidData_ShouldStartRecording() - { - // Arrange - var cuesheet = new Cuesheet(); - - // Act - cuesheet.StartRecording(); - - // Assert - Assert.IsTrue(cuesheet.IsRecording); - Assert.IsNotNull(cuesheet.RecordingStart); - } - - [TestMethod] - public void StopRecording_WithRecordingRunning_ShouldStopRecording() - { - // Arrange - var cuesheet = new Cuesheet(); - cuesheet.StartRecording(); - var track = new Track(); - cuesheet.AddTrack(track); - bool isRecordingChangedEventFired = false; - cuesheet.IsRecordingChanged += (sender, args) => isRecordingChangedEventFired = true; - - // Act - cuesheet.StopRecording(); - - // Assert - Assert.IsFalse(cuesheet.IsRecording); - Assert.IsNull(cuesheet.RecordingStart); - Assert.IsTrue(isRecordingChangedEventFired); - Assert.IsNotNull(track.End); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); } [TestMethod] - public void StartRecording_ShouldNotStartIfAlreadyRecording() + public void Validate_ArtistNull_ReturnsValidationStatusError() { // Arrange var cuesheet = new Cuesheet(); - cuesheet.StartRecording(); - // Act - cuesheet.StartRecording(); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Artist)); // Assert - Assert.IsTrue(cuesheet.IsRecording); - Assert.IsNotNull(cuesheet.RecordingStart); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); } [TestMethod] - public void StartRecording_WithTrackAlreadyAdded_ShouldNotStartRecording() + public void Validate_TitleNull_ReturnsValidationStatusError() { // Arrange var cuesheet = new Cuesheet(); - cuesheet.AddTrack(new Track()); - // Act - cuesheet.StartRecording(); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Title)); // Assert - Assert.IsFalse(cuesheet.IsRecording); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); } [TestMethod] - public void StartRecording_WithAudiofile_ShouldStartRecording() + public void Validate_CataloguenumberWithLetters_ReturnsValidationStatusError() { // Arrange - var cuesheet = new Cuesheet + var cuesheet = new Cuesheet() { - Audiofile = new Audiofile("test") + Cataloguenumber = nameof(Validate_CataloguenumberWithLetters_ReturnsValidationStatusError) }; - // Act - cuesheet.StartRecording(); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Cataloguenumber)); // Assert - Assert.IsTrue(cuesheet.IsRecording); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); + Assert.AreEqual("{0} must only contain numbers!", validationResult.ValidationMessages.First().Message); + Assert.AreEqual(nameof(Cuesheet.Cataloguenumber), validationResult.ValidationMessages.First().Parameter?.First().ToString()); } [TestMethod] - public void RecalculateLastTrackEnd_SingleTrackWithAudiofile_EndSetToAudiofileDuration() + public void Validate_CataloguenumberLengthUnequal13_ReturnsValidationStatusError() { // Arrange - var cuesheet = new Cuesheet + var cuesheet = new Cuesheet() { - Audiofile = new("Test.mp3", nameof(RecalculateLastTrackEnd_SingleTrackWithAudiofile_EndSetToAudiofileDuration), new AudioCodec("audio/mpeg", ".mp3", "AudioCodec MP3"), TimeSpan.FromMinutes(5)) + Cataloguenumber = "012345678" }; - var track = new Track { Position = 1, Begin = TimeSpan.Zero }; - cuesheet.AddTrack(track); - // Act - cuesheet.RecalculateLastTrackEnd(); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Cataloguenumber)); // Assert - Assert.AreEqual(TimeSpan.FromMinutes(5), track.End); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); + Assert.AreEqual("{0} has an invalid length. Allowed length is {1}!", validationResult.ValidationMessages.First().Message); + Assert.AreEqual(nameof(Cuesheet.Cataloguenumber), validationResult.ValidationMessages.First().Parameter?.First().ToString()); + Assert.AreEqual(13, validationResult.ValidationMessages.First().Parameter?.Last()); } [TestMethod] - public void RecalculateLastTrackEnd_MultipleTracks_EndSetCorrectly() + public void Validate_TracksEmpty_ReturnsValidationStatusError() { // Arrange var cuesheet = new Cuesheet(); - var track1 = new Track { Position = 1, Begin = TimeSpan.Zero, End = TimeSpan.FromMinutes(2) }; - var track2 = new Track { Position = 2, Begin = TimeSpan.FromMinutes(2) }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - // Act - cuesheet.RecalculateLastTrackEnd(); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); // Assert - Assert.AreEqual(TimeSpan.FromMinutes(2), track1.End); - Assert.IsNull(track2.End); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); + Assert.AreEqual("{0} has invalid Count ({1})!", validationResult.ValidationMessages.First().Message); + Assert.AreEqual(nameof(Cuesheet.Tracks), validationResult.ValidationMessages.First().Parameter?.First().ToString()); + Assert.AreEqual(0, validationResult.ValidationMessages.First().Parameter?.Last()); } [TestMethod] - public void RecalculateLastTrackEnd_MultipleTracksWithAudiofile_LastTrackEndSetToAudiofileDuration() + public void Validate_TracksWithSamePosition_ReturnsValidationStatusError() { // Arrange - var cuesheet = new Cuesheet + var track1 = new Track() { - Audiofile = new("Test.mp3", nameof(RecalculateLastTrackEnd_SingleTrackWithAudiofile_EndSetToAudiofileDuration), new AudioCodec("audio/mpeg", ".mp3", "AudioCodec MP3"), TimeSpan.FromMinutes(5)) + Position = 1 }; - var track1 = new Track { Position = 1, Begin = TimeSpan.Zero, End = TimeSpan.FromMinutes(2) }; - var track2 = new Track { Position = 2, Begin = TimeSpan.FromMinutes(2) }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - + var track2 = new Track() + { + Position = 1 + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; // Act - cuesheet.RecalculateLastTrackEnd(); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); // Assert - Assert.AreEqual(TimeSpan.FromMinutes(2), track1.End); - Assert.AreEqual(TimeSpan.FromMinutes(5), track2.End); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); + Assert.AreEqual("{0} {1} '{2}' is used also by {3}({4},{5},{6},{7},{8}). Positions must be unique!", validationResult.ValidationMessages.First().Message); } [TestMethod] - public void RecalculateLastTrackEnd_TracksWithLinkedPreviousTrack_PropertiesRecalculatedCorrectly() + public void Validate_TracksOverlapping_ReturnsValidationStatusError() { // Arrange - var cuesheet = new Cuesheet(); - var track1 = new Track { Position = 1, Begin = TimeSpan.Zero, End = TimeSpan.FromMinutes(2) }; - var track2 = new Track { Position = 2, IsLinkedToPreviousTrack = true }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 45) + }; + var track2 = new Track() + { + Position = 2, + Begin = new TimeSpan(0, 3, 42), + End = new TimeSpan(0, 6, 32) + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; // Act - cuesheet.RecalculateLastTrackEnd(); - + var validationResult = cuesheet.Validate(nameof(Cuesheet.Tracks)); // Assert - Assert.AreEqual(TimeSpan.FromMinutes(2), track1.End); - Assert.AreEqual(TimeSpan.FromMinutes(2), track2.Begin); + Assert.AreEqual(ValidationStatus.Error, validationResult.Status); + Assert.AreEqual("{0}({1},{2},{3},{4},{5}) is overlapping with {0}({6},{7},{8},{9},{10}). Please make shure the timeinterval is only used once!", validationResult.ValidationMessages.First().Message); } } } \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Model/AudioCuesheet/TrackTests.cs b/AudioCuesheetEditor.Tests/Model/AudioCuesheet/TrackTests.cs index fe4c93ca..d29aa2d1 100644 --- a/AudioCuesheetEditor.Tests/Model/AudioCuesheet/TrackTests.cs +++ b/AudioCuesheetEditor.Tests/Model/AudioCuesheet/TrackTests.cs @@ -15,11 +15,8 @@ //. using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.Entity; -using AudioCuesheetEditor.Tests.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Collections.Generic; -using System.Linq; namespace AudioCuesheetEditor.Tests.Model.AudioCuesheet { @@ -27,201 +24,52 @@ namespace AudioCuesheetEditor.Tests.Model.AudioCuesheet public class TrackTests { [TestMethod()] - public void TrackTest() - { - //Tests for Length - var track = new Track(); - Assert.IsNull(track.Length); - track.Length = new TimeSpan(0, 2, 30); - Assert.IsNotNull(track.Length); - Assert.IsNotNull(track.Begin); - Assert.IsNotNull(track.End); - Assert.AreEqual(TimeSpan.Zero, track.Begin); - Assert.AreEqual(new TimeSpan(0, 2, 30), track.End); - Assert.AreEqual(new TimeSpan(0, 2, 30), track.Length); - track.Begin = new TimeSpan(0, 25, 0); - Assert.IsNull(track.Length); - track.Length = new TimeSpan(0, 3, 30); - Assert.IsNotNull(track.Length); - Assert.AreEqual(new TimeSpan(0, 3, 30), track.Length); - Assert.AreEqual(new TimeSpan(0, 28, 30), track.End); - track.Begin = null; - Assert.IsNull(track.Length); - track.Length = new TimeSpan(0, 5, 15); - Assert.AreEqual(new TimeSpan(0, 28, 30), track.End); - Assert.AreEqual(new TimeSpan(0, 5, 15), track.Length); - Assert.AreEqual(new TimeSpan(0, 23, 15), track.Begin); - } - - [TestMethod()] - public void SetLengthTest() + public void Validate_PositionNull_ReturnsValidationStatusError() { + // Arrange var track = new Track(); - Assert.IsNull(track.Length); - Assert.IsNull(track.Begin); - Assert.IsNull(track.End); - track.Begin = new TimeSpan(0, 2, 43); - track.End = new TimeSpan(0, 5, 23); - Assert.IsNotNull(track.Length); - track.Length = new TimeSpan(0, 2, 0); - Assert.AreEqual(new TimeSpan(0, 4, 43), track.End); + // Act + var endValidationResult = track.Validate(nameof(Track.Position)); + // Assert + Assert.AreEqual(ValidationStatus.Error, endValidationResult.Status); } [TestMethod()] - public void LinkTrackTest() + public void Validate_Position0_ReturnsValidationStatusError() { - //Test LinkedPreviousTrack - var track = new Track - { - Begin = TimeSpan.Zero, - Position = 1, - End = new TimeSpan(0, 3, 23) - }; - Assert.IsFalse(track.IsLinkedToPreviousTrack); - var track2 = new Track - { - Begin = track.End, - End = new TimeSpan(0, 5, 45), - Position = 2 - }; - Assert.IsFalse(track2.IsLinkedToPreviousTrack); - bool eventFired = false; - track2.IsLinkedToPreviousTrackChanged += delegate + // Arrange + var track = new Track() { - eventFired = true; + Position = 0 }; - track2.IsLinkedToPreviousTrack = true; - Assert.IsTrue(eventFired); + // Act + var endValidationResult = track.Validate(nameof(Track.Position)); + // Assert + Assert.AreEqual(ValidationStatus.Error, endValidationResult.Status); } [TestMethod()] - public void CheckPositionInCuesheetTest() + public void Validate_PositionOvverlapping_ReturnsValidationStatusError() { - var cuesheet = new Cuesheet(); - var track1 = new Track - { - Position = 3, - End = new TimeSpan(0, 5, 0) - }; - var track2 = new Track + // Arrange + var track1 = new Track() { - Position = 2, - Begin = track1.End, - End = new TimeSpan(0, 8, 23) + Position = 1 }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - var validationResult = track1.Validate(nameof(Track.Position)); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "Track({0},{1},{2},{3},{4}) does not have the correct position '{5}'!" - && x.Parameter != null && x.Parameter[0].Equals(track1.Position) && x.Parameter[3].Equals(track1.Begin) && x.Parameter[4].Equals(track1.End) && x.Parameter[5].Equals(1))); - track1.Position = 1; - validationResult = track1.Validate(nameof(Track.Position)); - Assert.AreEqual(ValidationStatus.Success, validationResult.Status); - validationResult = track2.Validate(nameof(Track.Position)); - Assert.AreEqual(ValidationStatus.Success, validationResult.Status); - track1.Position = 3; - track2.Position = 5; - validationResult = track1.Validate(nameof(Track.Position)); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "Track({0},{1},{2},{3},{4}) does not have the correct position '{5}'!" - && x.Parameter != null && x.Parameter[0].Equals(track1.Position) && x.Parameter[3].Equals(track1.Begin) && x.Parameter[4].Equals(track1.End) && x.Parameter[5].Equals(1))); - validationResult = track2.Validate(nameof(Track.Position)); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Message == "Track({0},{1},{2},{3},{4}) does not have the correct position '{5}'!" - && x.Parameter != null && x.Parameter[0].Equals(track2.Position) && x.Parameter[3].Equals(track2.Begin) && x.Parameter[4].Equals(track2.End) && x.Parameter[5].Equals(2))); - cuesheet = new Cuesheet(); - track1 = new Track() + var track = new Track() { - Artist = "Testartist 1", - Title = "Testtitle 1" + Position = 1 }; - cuesheet.AddTrack(track1); - track2 = new Track() + var cuesheet = new Cuesheet() { - Artist = "Testartist 2", - Title = "Testtitle 2" + Tracks = [track1, track] }; - cuesheet.AddTrack(track2); - Assert.IsNotNull(track1.Begin); - Assert.IsNull(track1.End); - Assert.IsNull(track2.Begin); - Assert.IsNull(track2.End); - validationResult = track1.Validate(nameof(Track.Position)); - Assert.AreEqual(ValidationStatus.Success, validationResult.Status); - validationResult = track2.Validate(nameof(Track.Position)); - Assert.AreEqual(ValidationStatus.Success, validationResult.Status); - } - - [TestMethod()] - public void CopyValuesTest() - { - var sourceTrack = new Track(); - var destinyTrack = new Track(); - Assert.AreNotEqual(sourceTrack, destinyTrack); - sourceTrack.Cuesheet = new Cuesheet(); - sourceTrack.IsLinkedToPreviousTrack = true; - sourceTrack.Position = 23; - sourceTrack.Artist = "Testartist"; - sourceTrack.Title = "Testtitle"; - sourceTrack.Begin = new TimeSpan(0, 2, 10); - sourceTrack.End = new TimeSpan(0, 5, 32); - sourceTrack.PreGap = new TimeSpan(0, 0, 10); - sourceTrack.PostGap = new TimeSpan(0, 0, 12); - Assert.IsNull(destinyTrack.Cuesheet); - destinyTrack.CopyValues(sourceTrack); - Assert.AreEqual(sourceTrack.IsLinkedToPreviousTrack, destinyTrack.IsLinkedToPreviousTrack); - Assert.AreEqual(sourceTrack.Cuesheet, destinyTrack.Cuesheet); - Assert.AreEqual(sourceTrack.Position, destinyTrack.Position); - Assert.AreEqual(sourceTrack.Artist, destinyTrack.Artist); - Assert.AreEqual(sourceTrack.Title, destinyTrack.Title); - Assert.AreEqual(sourceTrack.Begin, destinyTrack.Begin); - Assert.AreEqual(sourceTrack.End, destinyTrack.End); - Assert.AreEqual(sourceTrack.PreGap, destinyTrack.PreGap); - Assert.AreEqual(sourceTrack.PostGap, destinyTrack.PostGap); - var destinationTracks = new List() { new(), new(), new() }; - destinationTracks.ForEach(x => x.CopyValues(sourceTrack, useInternalSetters: Track.AllPropertyNames)); - Assert.IsTrue(destinationTracks.All(x => x.IsLinkedToPreviousTrack == sourceTrack.IsLinkedToPreviousTrack)); - Assert.IsTrue(destinationTracks.All(x => x.Cuesheet == sourceTrack.Cuesheet)); - Assert.IsTrue(destinationTracks.All(x => x.Position == sourceTrack.Position)); - Assert.IsTrue(destinationTracks.All(x => x.Artist == sourceTrack.Artist)); - Assert.IsTrue(destinationTracks.All(x => x.Title == sourceTrack.Title)); - Assert.IsTrue(destinationTracks.All(x => x.Begin == sourceTrack.Begin)); - Assert.IsTrue(destinationTracks.All(x => x.End == sourceTrack.End)); - Assert.IsTrue(destinationTracks.All(x => x.PreGap == sourceTrack.PreGap)); - Assert.IsTrue(destinationTracks.All(x => x.PostGap == sourceTrack.PostGap)); - } - - [TestMethod()] - public void CloneTest() - { - var track = new Track() - { - Begin = TimeSpan.Zero, - Artist = "Testartist", - Title = "Testttitle" - }; - var cuesheet = new Cuesheet(); - cuesheet.AddTrack(track); - var validationResult = track.Validate(); - Assert.AreEqual(ValidationStatus.Error, validationResult.Status); - Assert.IsTrue(validationResult.ValidationMessages?.Any(x => x.Parameter != null && x.Parameter.Contains(nameof(Track.End)))); - var clone = track.Clone(); - clone.End = new TimeSpan(0, 2, 32); - validationResult = clone.Validate(); - Assert.AreEqual(ValidationStatus.Success, validationResult.Status); - Assert.IsNull(track.End); - } - - [TestMethod()] - public void SetCuesheetTest() - { - var track = new Track(); - Assert.IsNull(track.Cuesheet); - var cuesheet = new Cuesheet(); - cuesheet.AddTrack(track); - Assert.AreEqual(cuesheet, track.Cuesheet); - track.Cuesheet = new Cuesheet(); - Assert.AreEqual(cuesheet, track.Cuesheet); - track.Cuesheet = null; - Assert.IsNull(track.Cuesheet); + track1.Cuesheet = cuesheet; + track.Cuesheet = cuesheet; + // Act + var endValidationResult = track.Validate(nameof(Track.Position)); + // Assert + Assert.AreEqual(ValidationStatus.Error, endValidationResult.Status); } [TestMethod()] diff --git a/AudioCuesheetEditor.Tests/Model/IO/ProjectfileTests.cs b/AudioCuesheetEditor.Tests/Model/IO/ProjectfileTests.cs index 37848f93..335266f1 100644 --- a/AudioCuesheetEditor.Tests/Model/IO/ProjectfileTests.cs +++ b/AudioCuesheetEditor.Tests/Model/IO/ProjectfileTests.cs @@ -41,11 +41,11 @@ public void GenerateFile_WithoutSections_GeneratesOneFile() Cataloguenumber = "A123" }; var begin = TimeSpan.Zero; - for (int i = 1; i <= 10; i++) + for (ushort i = 1; i <= 10; i++) { var track = new Track { - Position = (uint)i, + Position = i, Artist = string.Format("Artist {0}", i), Title = string.Format("Title {0}", i), Begin = begin @@ -60,7 +60,7 @@ public void GenerateFile_WithoutSections_GeneratesOneFile() track.Flags = flags; begin = begin.Add(new TimeSpan(0, i, i)); track.End = begin; - cuesheet.AddTrack(track); + cuesheet.Tracks = cuesheet.Tracks.Append(track); } // Act var projectFile = new Projectfile(cuesheet); @@ -85,6 +85,7 @@ public void ImportFile_ValidProjectfile_ShouldImportFile() // Assert Assert.IsNotNull(cuesheet); Assert.IsTrue(cuesheet.Tracks.All(x => x.Cuesheet == cuesheet)); + Assert.IsTrue(cuesheet.Tracks.All(x => x.IsLinkedToPreviousTrack)); Assert.AreEqual("CuesheetArtist", cuesheet.Artist); Assert.AreEqual("CuesheetTitle", cuesheet.Title); Assert.AreEqual("AudioFile.mp3", cuesheet.Audiofile?.Name); @@ -95,38 +96,7 @@ public void ImportFile_ValidProjectfile_ShouldImportFile() Assert.IsTrue(cuesheet.Tracks.ElementAt(3).Flags.Contains(Flag.FourCH)); Assert.AreEqual("Artist 10", cuesheet.Tracks.Last().Artist); Assert.AreEqual(new TimeSpan(0, 55, 55), cuesheet.Tracks.Last().End); - Assert.IsTrue(ReferenceEquals(cuesheet.Tracks.First(), cuesheet.GetPreviousLinkedTrack(cuesheet.Tracks.ElementAt(1)))); - Assert.AreEqual(cuesheet.Tracks.First(), cuesheet.GetPreviousLinkedTrack(cuesheet.Tracks.ElementAt(1))); - Assert.AreEqual((uint)10, cuesheet.Tracks.Last().Position); - } - - [TestMethod()] - public void ImportFile_ValidProjectfileWithSections_ShouldImportFile() - { - //Arrange - var fileContent = "{\"Tracks\":[{\"Position\":1,\"Artist\":\"Artist 1\",\"Title\":\"Title 1\",\"Begin\":\"00:00:00\",\"End\":\"00:01:01\",\"Flags\":[\"4CH\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":2,\"Artist\":\"Artist 2\",\"Title\":\"Title 2\",\"Begin\":\"00:01:01\",\"End\":\"00:03:03\",\"Flags\":[\"4CH\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":3,\"Artist\":\"Artist 3\",\"Title\":\"Title 3\",\"Begin\":\"00:03:03\",\"End\":\"00:06:06\",\"Flags\":[\"4CH\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":4,\"Artist\":\"Artist 4\",\"Title\":\"Title 4\",\"Begin\":\"00:06:06\",\"End\":\"00:10:10\",\"Flags\":[\"4CH\",\"DCP\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":5,\"Artist\":\"Artist 5\",\"Title\":\"Title 5\",\"Begin\":\"00:10:10\",\"End\":\"00:15:15\",\"Flags\":[\"4CH\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":6,\"Artist\":\"Artist 6\",\"Title\":\"Title 6\",\"Begin\":\"00:15:15\",\"End\":\"00:21:21\",\"Flags\":[\"4CH\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":7,\"Artist\":\"Artist 7\",\"Title\":\"Title 7\",\"Begin\":\"00:21:21\",\"End\":\"00:28:28\",\"Flags\":[\"4CH\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":8,\"Artist\":\"Artist 8\",\"Title\":\"Title 8\",\"Begin\":\"00:28:28\",\"End\":\"00:36:36\",\"Flags\":[\"4CH\",\"DCP\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":9,\"Artist\":\"Artist 9\",\"Title\":\"Title 9\",\"Begin\":\"00:36:36\",\"End\":\"00:45:45\",\"Flags\":[\"4CH\"],\"IsLinkedToPreviousTrack\":true},{\"Position\":10,\"Artist\":\"Artist 10\",\"Title\":\"Title 10\",\"Begin\":\"00:45:45\",\"End\":\"00:55:55\",\"Flags\":[\"4CH\",\"DCP\"],\"IsLinkedToPreviousTrack\":true}],\"Artist\":\"CuesheetArtist\",\"Title\":\"CuesheetTitle\",\"Audiofile\":{\"Name\":\"AudioFile.mp3\"},\"CDTextfile\":{\"Name\":\"CDTextfile.cdt\"},\"Cataloguenumber\":\"A123\",\"Sections\":[{\"Artist\":\"CuesheetArtist\",\"Title\":\"CuesheetTitle\",\"Begin\":\"00:30:00\"},{\"Artist\":\"CuesheetArtist\",\"Title\":\"CuesheetTitle\",\"Begin\":\"01:00:00\"}]}"; - // Act - var cuesheet = Projectfile.ImportFile(fileContent); - // Assert - Assert.IsNotNull(cuesheet); - Assert.IsTrue(cuesheet.Tracks.All(x => x.Cuesheet == cuesheet)); - Assert.AreEqual("CuesheetArtist", cuesheet.Artist); - Assert.AreEqual("CuesheetTitle", cuesheet.Title); - Assert.AreEqual("AudioFile.mp3", cuesheet.Audiofile?.Name); - Assert.AreEqual("A123", cuesheet.Cataloguenumber); - Assert.AreEqual(2, cuesheet.Validate(nameof(Cuesheet.Cataloguenumber)).ValidationMessages?.Count); - Assert.HasCount(10, cuesheet.Tracks); - Assert.IsTrue(cuesheet.Tracks.ElementAt(3).Flags.Contains(Flag.DCP)); - Assert.IsTrue(cuesheet.Tracks.ElementAt(3).Flags.Contains(Flag.FourCH)); - Assert.AreEqual("Artist 10", cuesheet.Tracks.Last().Artist); - Assert.AreEqual(new TimeSpan(0, 55, 55), cuesheet.Tracks.Last().End); - Assert.IsTrue(ReferenceEquals(cuesheet.Tracks.First(), cuesheet.GetPreviousLinkedTrack(cuesheet.Tracks.ElementAt(1)))); - Assert.AreEqual(cuesheet.Tracks.First(), cuesheet.GetPreviousLinkedTrack(cuesheet.Tracks.ElementAt(1))); - Assert.AreEqual((uint)10, cuesheet.Tracks.Last().Position); - //TODOD - //Assert.HasCount(2, cuesheet.Sections); - //Assert.AreEqual(new TimeSpan(0, 30, 0), cuesheet.Sections.First().Begin); - //Assert.AreEqual(new TimeSpan(1, 0, 0), cuesheet.Sections.Last().Begin); + Assert.AreEqual((ushort)10, cuesheet.Tracks.Last().Position); } } } \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Services/AudioCuesheet/CuesheetManagerTests.cs b/AudioCuesheetEditor.Tests/Services/AudioCuesheet/CuesheetManagerTests.cs new file mode 100644 index 00000000..f3496ee8 --- /dev/null +++ b/AudioCuesheetEditor.Tests/Services/AudioCuesheet/CuesheetManagerTests.cs @@ -0,0 +1,991 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//. +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.IO.Audio; +using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Services; +using AudioCuesheetEditor.Services.AudioCuesheet; +using AudioCuesheetEditor.Services.UI; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace AudioCuesheetEditor.Tests.Services.AudioCuesheet +{ + [TestClass] + public class CuesheetManagerTests + { + private readonly CuesheetManager _cuesheetManager; + private readonly Mock _traceChangeManager; + private readonly Mock _sessionStateContainer; + private readonly Mock _trackManager; + + public CuesheetManagerTests() + { + _traceChangeManager = new(); + _sessionStateContainer = new(); + _trackManager = new(); + _trackManager.Setup(x => x.SetProperty(It.IsAny(),It.IsAny>>(),It.IsAny())) + .Callback((Track track, LambdaExpression propExpr, object value) => + { + var memberExpression = propExpr.Body as MemberExpression; + if (memberExpression?.Member is PropertyInfo propertyInfo) + { + propertyInfo.SetValue(track, value); + } + }); + _trackManager.Setup(x => x.GetPreviousLinkedTrack(It.IsAny())).Returns(delegate (Track track) + { + Track? previousLinkedTrack = null; + if (track.IsLinkedToPreviousTrack) + { + if (track.Position.HasValue) + { + previousLinkedTrack = track.Cuesheet?.Tracks.LastOrDefault(x => x.Position == track.Position - 1 && Equals(x, track) == false); + } + else + { + if (track.Begin.HasValue) + { + previousLinkedTrack = track.Cuesheet?.Tracks.OrderBy(x => x.End).LastOrDefault(x => x.End <= track.Begin); + } + } + } + return previousLinkedTrack; + }); + _cuesheetManager = new(_traceChangeManager.Object, _sessionStateContainer.Object, _trackManager.Object); + } + + [TestMethod] + public void SetProperty_NewValue_ChangesPropertyAndSetsTracedChange() + { + // Arrange + var cuesheet = new Cuesheet(); + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + // Act + _cuesheetManager.SetProperty(x => x.Artist, "Artist"); + // Assert + Assert.AreEqual("Artist", cuesheet.Artist); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Cuesheet.Artist))), Times.Once); + } + + [TestMethod] + public void SetProperty_EqualValue_DoesntChangeProperty() + { + // Arrange + var cuesheet = new Cuesheet() + { + Artist = "Artist" + }; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + // Act + _cuesheetManager.SetProperty(x => x.Artist, cuesheet.Artist); + // Assert + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Cuesheet.Artist))), Times.Never); + } + + [TestMethod] + public void SetProperty_AudiofileWithDuration_SetsLastTrackEndAlso() + { + // Arrange + var track = new Track() + { + Position = 1 + }; + var cuesheet = new Cuesheet() + { + Tracks = [track] + }; + track.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + var duration = new TimeSpan(0, 5, 23); + var audiofile = new Audiofile("Test.mp3", nameof(SetProperty_AudiofileWithDuration_SetsLastTrackEndAlso), Audiofile.AudioCodecs.First(), duration); + // Act + _cuesheetManager.SetProperty(x => x.Audiofile, audiofile); + // Assert + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Cuesheet.Audiofile))), Times.Once); + Assert.AreEqual(duration, track.End); + } + + [TestMethod] + public void IsRecordingPossible_WithoutTracks_ReturnsSuccess() + { + // Arrange + var cuesheet = new Cuesheet(); + _sessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); + // Act + var result = _cuesheetManager.IsRecordingPossible; + // Assert + Assert.IsTrue(result.IsSuccess); + } + + [TestMethod] + public void IsRecordingPossible_WithTracks_ReturnsFailure() + { + // Arrange + var cuesheet = new Cuesheet() + { + Tracks = [ + new() + ] + }; + _sessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); + // Act + var result = _cuesheetManager.IsRecordingPossible; + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual("Cuesheet already contains tracks!", result.Error!.Message); + } + + [TestMethod] + public void StartRecording_RecordingPossible_ReturnsSuccess() + { + // Arrange + var cuesheet = new Cuesheet(); + _sessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); + var isRecordingChanged = false; + _cuesheetManager.IsRecordingChanged += delegate + { + isRecordingChanged = true; + }; + // Act + var result = _cuesheetManager.StartRecording(); + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.IsTrue(isRecordingChanged); + Assert.IsNotNull(cuesheet.RecordingStart); + } + + [TestMethod] + public void StartRecording_RecordingNotPossible_ReturnsFailure() + { + // Arrange + var cuesheet = new Cuesheet() + { + RecordingStart = DateTime.UtcNow.AddDays(-1) + }; + _sessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); + var isRecordingChanged = false; + _cuesheetManager.IsRecordingChanged += delegate + { + isRecordingChanged = true; + }; + // Act + var result = _cuesheetManager.StartRecording(); + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.IsFalse(isRecordingChanged); + Assert.AreEqual("Record is already running!", result.Error!.Message); + } + + [TestMethod] + public void StopRecording_WithActiveRecording_StopsRecordAndSetsTrackDetails() + { + // Arrange + var cuesheet = new Cuesheet() + { + RecordingStart = DateTime.UtcNow.AddDays(-1), + Tracks = [ + new() { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12) + }, + new() { + Position = 2, + Begin = new TimeSpan(0, 3, 12) + } + ] + }; + _sessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); + var isRecordingChanged = false; + _cuesheetManager.IsRecordingChanged += delegate + { + isRecordingChanged = true; + }; + // Act + _cuesheetManager.StopRecording(); + // Assert + Assert.IsFalse(cuesheet.IsRecording); + Assert.IsNull(cuesheet.RecordingStart); + Assert.IsTrue(isRecordingChanged); + Assert.IsNotNull(cuesheet.Tracks.Last().End); + } + + [TestMethod] + public void StopRecording_WithoutActiveRecording_ChangesNothing() + { + // Arrange + var cuesheet = new Cuesheet() + { + Tracks = [ + new() { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12) + }, + new() { + Position = 2, + Begin = new TimeSpan(0, 3, 12) + } + ] + }; + _sessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); + var isRecordingChanged = false; + _cuesheetManager.IsRecordingChanged += delegate + { + isRecordingChanged = true; + }; + // Act + _cuesheetManager.StopRecording(); + // Assert + Assert.IsFalse(isRecordingChanged); + Assert.IsNull(cuesheet.Tracks.Last().End); + } + + [TestMethod] + public void AddTrack_FirstTrack_AddsNewTrackWithCalulatedTrackProperties() + { + // Arrange + var duration = new TimeSpan(0, 27, 56); + var cuesheet = new Cuesheet() + { + Audiofile = new("Audio.mp3", nameof(AddTrack_FirstTrack_AddsNewTrackWithCalulatedTrackProperties), Audiofile.AudioCodecs.First(), duration) + }; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + var track = new Track(); + // Act + _cuesheetManager.AddTrack(track); + // Assert + Assert.HasCount(1, cuesheet.Tracks); + Assert.AreEqual((ushort)1, cuesheet.Tracks.First().Position); + Assert.AreEqual(TimeSpan.Zero, cuesheet.Tracks.First().Begin); + Assert.AreEqual(duration, cuesheet.Tracks.First().End); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PreviousValue == Enumerable.Empty() && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + } + + [TestMethod] + public void AddTrack_AddToPreviousTracks_AddsNewTrackWithCalulatedTrackProperties() + { + // Arrange + var duration = new TimeSpan(0, 27, 56); + var tracks = new List + { + new() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(3, 12, 0) + } + }; + var cuesheet = new Cuesheet() + { + Tracks = tracks, + Audiofile = new("Audio.mp3", nameof(AddTrack_AddToPreviousTracks_AddsNewTrackWithCalulatedTrackProperties), Audiofile.AudioCodecs.First(), duration) + }; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + var track = new Track(); + // Act + _cuesheetManager.AddTrack(track); + // Assert + Assert.HasCount(2, cuesheet.Tracks); + Assert.AreEqual((ushort)2, cuesheet.Tracks.Last().Position); + Assert.AreEqual(cuesheet.Tracks.First().End, cuesheet.Tracks.Last().Begin); + Assert.AreEqual(duration, cuesheet.Tracks.Last().End); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PreviousValue == tracks && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + } + + [TestMethod] + public void AddTrack_DuringRecording_AddsNewTrackWithCalulatedTrackProperties() + { + // Arrange + var tracks = new List + { + new() + { + Position = 1, + Begin = TimeSpan.Zero + } + }; + var cuesheet = new Cuesheet() + { + RecordingStart = DateTime.UtcNow, + Tracks = tracks + }; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + var track = new Track(); + // Act + _cuesheetManager.AddTrack(track); + // Assert + Assert.HasCount(2, cuesheet.Tracks); + Assert.AreEqual((ushort)2, cuesheet.Tracks.Last().Position); + Assert.IsNotNull(cuesheet.Tracks.First().End); + Assert.IsNotNull(cuesheet.Tracks.Last().Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PreviousValue == tracks && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + } + + [TestMethod] + public void AddTrack_FirstTrackImporting_AddsNewTrackWithCalulatedTrackProperties() + { + // Arrange + var duration = new TimeSpan(0, 27, 56); + var importCuesheet = new Cuesheet() + { + Audiofile = new("Audio.mp3", nameof(AddTrack_FirstTrackImporting_AddsNewTrackWithCalulatedTrackProperties), Audiofile.AudioCodecs.First(), duration) + }; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(importCuesheet); + var track = new Track(); + // Act + _cuesheetManager.AddTrack(track); + // Assert + Assert.HasCount(1, importCuesheet.Tracks); + Assert.AreEqual((ushort)1, importCuesheet.Tracks.First().Position); + Assert.AreEqual(TimeSpan.Zero, importCuesheet.Tracks.First().Begin); + Assert.AreEqual(duration, importCuesheet.Tracks.First().End); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == importCuesheet && y.TraceableChange.PreviousValue == Enumerable.Empty() && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + } + + [TestMethod] + public void RemoveTracks_WithAttachedTracks_RemovesTracksAndRecalculatesRemainingTrackProperties() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12), + IsLinkedToPreviousTrack = true + }; + var track2 = new Track() + { + Position = 2, + Begin = new TimeSpan(0, 3, 12), + End = new TimeSpan(0, 7, 34), + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = new TimeSpan(0, 7, 34), + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var track4 = new Track() + { + Position = 4, + Begin = new TimeSpan(0, 10, 4), + End = new TimeSpan(0, 14, 54), + IsLinkedToPreviousTrack = true + }; + var track5 = new Track() + { + Position = 5, + Begin = new TimeSpan(0, 14, 54), + IsLinkedToPreviousTrack = true + }; + var previousValue = new List() { track1, track2, track3, track4, track5 }; + var duration = new TimeSpan(0, 19, 38); + var cuesheet = new Cuesheet() + { + Tracks = previousValue, + Audiofile = new("Audio.mp3", nameof(RemoveTracks_WithAttachedTracks_RemovesTracksAndRecalculatesRemainingTrackProperties), Audiofile.AudioCodecs.First(), duration) + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + track4.Cuesheet = cuesheet; + track5.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + // Act + _cuesheetManager.RemoveTracks([track2, track4]); + // Assert + Assert.HasCount(3, cuesheet.Tracks); + Assert.IsTrue(cuesheet.Tracks.Contains(track1)); + Assert.IsTrue(cuesheet.Tracks.Contains(track3)); + Assert.IsTrue(cuesheet.Tracks.Contains(track5)); + Assert.AreEqual((ushort)1, track1.Position); + Assert.AreEqual(TimeSpan.Zero, track1.Begin); + Assert.AreEqual(track1.End, track3.Begin); + Assert.AreEqual((ushort)2, track3.Position); + Assert.AreEqual(track5.Begin, track3.End); + Assert.AreEqual((ushort)3, track5.Position); + Assert.AreEqual(duration, track5.End); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PreviousValue == previousValue && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + } + + [TestMethod] + public void RemoveTracks_DuringImport_RemovesTracksAndRecalculatesRemainingTrackProperties() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12), + IsLinkedToPreviousTrack = true + }; + var track2 = new Track() + { + Position = 2, + Begin = new TimeSpan(0, 3, 12), + End = new TimeSpan(0, 7, 34), + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = new TimeSpan(0, 7, 34), + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var track4 = new Track() + { + Position = 4, + Begin = new TimeSpan(0, 10, 4), + End = new TimeSpan(0, 14, 54), + IsLinkedToPreviousTrack = true + }; + var track5 = new Track() + { + Position = 5, + Begin = new TimeSpan(0, 14, 54), + IsLinkedToPreviousTrack = true + }; + var previousValue = new List() { track1, track2, track3, track4, track5 }; + var duration = new TimeSpan(0, 19, 38); + var importCuesheet = new Cuesheet() + { + Tracks = previousValue, + Audiofile = new("Audio.mp3", nameof(RemoveTracks_DuringImport_RemovesTracksAndRecalculatesRemainingTrackProperties), Audiofile.AudioCodecs.First(), duration) + }; + track1.Cuesheet = importCuesheet; + track2.Cuesheet = importCuesheet; + track3.Cuesheet = importCuesheet; + track4.Cuesheet = importCuesheet; + track5.Cuesheet = importCuesheet; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(importCuesheet); + // Act + _cuesheetManager.RemoveTracks([track2, track4]); + // Assert + Assert.HasCount(3, importCuesheet.Tracks); + Assert.IsTrue(importCuesheet.Tracks.Contains(track1)); + Assert.IsTrue(importCuesheet.Tracks.Contains(track3)); + Assert.IsTrue(importCuesheet.Tracks.Contains(track5)); + Assert.AreEqual((ushort)1, track1.Position); + Assert.AreEqual(TimeSpan.Zero, track1.Begin); + Assert.AreEqual(track1.End, track3.Begin); + Assert.AreEqual((ushort)2, track3.Position); + Assert.AreEqual(track5.Begin, track3.End); + Assert.AreEqual((ushort)3, track5.Position); + Assert.AreEqual(duration, track5.End); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == importCuesheet && y.TraceableChange.PreviousValue == previousValue && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + } + + [TestMethod] + public void IsMoveTracksUpPossible_TracksAbove_ReturnsTrue() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12), + IsLinkedToPreviousTrack = true + }; + var track2 = new Track() + { + Position = 2, + Begin = new TimeSpan(0, 3, 12), + End = new TimeSpan(0, 7, 34), + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = new TimeSpan(0, 7, 34), + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + // Act + var result = _cuesheetManager.IsMoveTracksUpPossible([track2, track3]); + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + public void IsMoveTracksUpPossible_NoTracksAbove_ReturnsFalse() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12), + IsLinkedToPreviousTrack = true + }; + var track2 = new Track() + { + Position = 2, + Begin = new TimeSpan(0, 3, 12), + End = new TimeSpan(0, 7, 34), + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = new TimeSpan(0, 7, 34), + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1, track3] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + // Act + var result = _cuesheetManager.IsMoveTracksUpPossible([track1, track2]); + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + public void IsMoveTracksUpPossible_EmptyCollection_ReturnsFalse() + { + // Arrange + // Act + var result = _cuesheetManager.IsMoveTracksUpPossible([]); + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + public void IsMoveTracksDownPossible_TracksBelow_ReturnsTrue() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12), + IsLinkedToPreviousTrack = true + }; + var track2 = new Track() + { + Position = 2, + Begin = new TimeSpan(0, 3, 12), + End = new TimeSpan(0, 7, 34), + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = new TimeSpan(0, 7, 34), + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + // Act + var result = _cuesheetManager.IsMoveTracksDownPossible([track2, track1]); + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + public void IsMoveTracksDownPossible_NoTracksBelow_ReturnsFalse() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12), + IsLinkedToPreviousTrack = true + }; + var track2 = new Track() + { + Position = 2, + Begin = new TimeSpan(0, 3, 12), + End = new TimeSpan(0, 7, 34), + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = new TimeSpan(0, 7, 34), + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1, track3] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + // Act + var result = _cuesheetManager.IsMoveTracksDownPossible([track3, track2]); + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + public void IsMoveTracksDownPossible_EmptyCollection_ReturnsFalse() + { + // Arrange + var cuesheet = new Cuesheet(); + // Act + var result = _cuesheetManager.IsMoveTracksDownPossible([]); + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + public void MoveTracksUp_TracksAbove_ReturnsSuccess() + { + // Arrange + var track1End = new TimeSpan(0, 3, 12); + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = track1End, + IsLinkedToPreviousTrack = true + }; + var track2End = new TimeSpan(0, 7, 34); + var track2 = new Track() + { + Position = 2, + Begin = track1End, + End = track2End, + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = track2End, + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + // Act + var result = _cuesheetManager.MoveTracksUp([track2, track3]); + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual((ushort?)1, track2.Position); + Assert.AreEqual(TimeSpan.Zero, track2.Begin); + Assert.AreEqual(track1End, track2.End); + Assert.AreEqual((ushort?)2, track3.Position); + Assert.AreEqual(track1End, track3.Begin); + Assert.AreEqual(track2End, track3.End); + Assert.AreEqual((ushort?)3, track1.Position); + Assert.AreEqual(track2End, track1.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + _traceChangeManager.VerifySet(x => x.BulkEdit = true, Times.Once); + _traceChangeManager.VerifySet(x => x.BulkEdit = false, Times.Once); + } + + [TestMethod] + public void MoveTracksUp_NoTracksAbove_ReturnsFailure() + { + // Arrange + var track1End = new TimeSpan(0, 3, 12); + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = track1End, + IsLinkedToPreviousTrack = true + }; + var track2End = new TimeSpan(0, 7, 34); + var track2 = new Track() + { + Position = 2, + Begin = track1End, + End = track2End, + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = track2End, + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.Cuesheet).Returns(cuesheet); + // Act + var result = _cuesheetManager.MoveTracksUp([track2, track1]); + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual(ErrorType.NotPossible, result.Error!.Type); + Assert.AreEqual((ushort?)1, track1.Position); + Assert.AreEqual(TimeSpan.Zero, track1.Begin); + Assert.AreEqual(track1End, track1.End); + Assert.AreEqual((ushort?)2, track2.Position); + Assert.AreEqual(track1End, track2.Begin); + Assert.AreEqual(track2End, track2.End); + Assert.AreEqual((ushort?)3, track3.Position); + Assert.AreEqual(track2End, track3.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = true, Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = false, Times.Never); + } + + [TestMethod] + public void MoveTracksUp_NoTracksSelected_ReturnsFailure() + { + // Arrange + var track1End = new TimeSpan(0, 3, 12); + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = track1End, + IsLinkedToPreviousTrack = true + }; + var track2End = new TimeSpan(0, 7, 34); + var track2 = new Track() + { + Position = 2, + Begin = track1End, + End = track2End, + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = track2End, + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.Cuesheet).Returns(cuesheet); + // Act + var result = _cuesheetManager.MoveTracksUp([]); + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual(ErrorType.NotPossible, result.Error!.Type); + Assert.AreEqual((ushort?)1, track1.Position); + Assert.AreEqual(TimeSpan.Zero, track1.Begin); + Assert.AreEqual(track1End, track1.End); + Assert.AreEqual((ushort?)2, track2.Position); + Assert.AreEqual(track1End, track2.Begin); + Assert.AreEqual(track2End, track2.End); + Assert.AreEqual((ushort?)3, track3.Position); + Assert.AreEqual(track2End, track3.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = true, Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = false, Times.Never); + } + + [TestMethod] + public void MoveTracksDown_TracksBelow_ReturnsSucess() + { + // Arrange + var track1End = new TimeSpan(0, 3, 12); + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = track1End, + IsLinkedToPreviousTrack = true + }; + var track2End = new TimeSpan(0, 7, 34); + var track2 = new Track() + { + Position = 2, + Begin = track1End, + End = track2End, + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = track2End, + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.GetActiveCuesheet()).Returns(cuesheet); + // Act + var result = _cuesheetManager.MoveTracksDown([track2, track1]); + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual((ushort?)1, track3.Position); + Assert.AreEqual(TimeSpan.Zero, track3.Begin); + Assert.AreEqual(track1End, track3.End); + Assert.AreEqual((ushort?)2, track1.Position); + Assert.AreEqual(track1End, track1.Begin); + Assert.AreEqual(track2End, track1.End); + Assert.AreEqual((ushort?)3, track2.Position); + Assert.AreEqual(track2End, track2.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Once); + _traceChangeManager.VerifySet(x => x.BulkEdit = true, Times.Once); + _traceChangeManager.VerifySet(x => x.BulkEdit = false, Times.Once); + } + + [TestMethod] + public void MoveTracksDown_NoTracksBelow_ReturnsFailure() + { + // Arrange + var track1End = new TimeSpan(0, 3, 12); + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = track1End, + IsLinkedToPreviousTrack = true + }; + var track2End = new TimeSpan(0, 7, 34); + var track2 = new Track() + { + Position = 2, + Begin = track1End, + End = track2End, + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = track2End, + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.Cuesheet).Returns(cuesheet); + // Act + var result = _cuesheetManager.MoveTracksDown([track2, track3]); + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual(ErrorType.NotPossible, result.Error!.Type); + Assert.AreEqual((ushort?)1, track1.Position); + Assert.AreEqual(TimeSpan.Zero, track1.Begin); + Assert.AreEqual(track1End, track1.End); + Assert.AreEqual((ushort?)2, track2.Position); + Assert.AreEqual(track1End, track2.Begin); + Assert.AreEqual(track2End, track2.End); + Assert.AreEqual((ushort?)3, track3.Position); + Assert.AreEqual(track2End, track3.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = true, Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = false, Times.Never); + } + + [TestMethod] + public void MoveTracksDown_NoTracksSelected_ReturnsFailure() + { + // Arrange + var track1End = new TimeSpan(0, 3, 12); + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = track1End, + IsLinkedToPreviousTrack = true + }; + var track2End = new TimeSpan(0, 7, 34); + var track2 = new Track() + { + Position = 2, + Begin = track1End, + End = track2End, + IsLinkedToPreviousTrack = true + }; + var track3 = new Track() + { + Position = 3, + Begin = track2End, + End = new TimeSpan(0, 10, 4), + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track3, track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + _sessionStateContainer.Setup(x => x.Cuesheet).Returns(cuesheet); + // Act + var result = _cuesheetManager.MoveTracksDown([]); + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual(ErrorType.NotPossible, result.Error!.Type); + Assert.AreEqual((ushort?)1, track1.Position); + Assert.AreEqual(TimeSpan.Zero, track1.Begin); + Assert.AreEqual(track1End, track1.End); + Assert.AreEqual((ushort?)2, track2.Position); + Assert.AreEqual(track1End, track2.Begin); + Assert.AreEqual(track2End, track2.End); + Assert.AreEqual((ushort?)3, track3.Position); + Assert.AreEqual(track2End, track3.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == cuesheet && y.TraceableChange.PropertyName == nameof(Cuesheet.Tracks))), Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = true, Times.Never); + _traceChangeManager.VerifySet(x => x.BulkEdit = false, Times.Never); + } + } +} diff --git a/AudioCuesheetEditor.Tests/Services/AudioCuesheet/TrackManagerTests.cs b/AudioCuesheetEditor.Tests/Services/AudioCuesheet/TrackManagerTests.cs new file mode 100644 index 00000000..f3fb1c6e --- /dev/null +++ b/AudioCuesheetEditor.Tests/Services/AudioCuesheet/TrackManagerTests.cs @@ -0,0 +1,637 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//. +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.AudioCuesheet.Import; +using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Services.AudioCuesheet; +using AudioCuesheetEditor.Services.UI; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; + +namespace AudioCuesheetEditor.Tests.Services.AudioCuesheet +{ + [TestClass] + public class TrackManagerTests + { + private readonly TrackManager _trackManager; + private readonly Mock _traceChangeManager; + + public TrackManagerTests() + { + _traceChangeManager = new Mock(); + _trackManager = new(_traceChangeManager.Object); + } + + [TestMethod] + public void SetProperty_NewValue_ChangesPropertyAndSetsTracedChange() + { + // Arrange + var track = new Track(); + var artist = "Artist 1"; + // Act + _trackManager.SetProperty(track, x => x.Artist, artist); + // Assert + Assert.AreEqual(artist, track.Artist); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Track.Artist))), Times.Once); + } + + [TestMethod] + public void SetProperty_EqualValue_DoesntChangeProperty() + { + // Arrange + var artist = "Artist 1"; + var track = new Track() + { + Artist = artist, + Position = 2 + }; + // Act + _trackManager.SetProperty(track, x => x.Artist, artist); + // Assert + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Track.Artist))), Times.Never); + } + + [TestMethod] + public void Clone_ImportTrack_ReturnsTrackClone() + { + // Arrange + var importTrack = new ImportTrack() + { + Artist = "Artist 1", + Title = "Title 1", + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 2, 54), + IsLinkedToPreviousTrack = true, + Position = 1, + PostGap = new TimeSpan(0, 0, 2), + PreGap = new TimeSpan(0, 0, 4), + Flags = [Flag.FourCH, Flag.DCP], + StartDateTime = new DateTime(2024, 2, 28, 19, 23, 54) + }; + // Act + var track = _trackManager.Clone(importTrack); + // Assert + Assert.AreEqual(importTrack.Artist, track.Artist); + Assert.AreEqual(importTrack.Title, track.Title); + Assert.AreEqual(importTrack.Begin, track.Begin); + Assert.AreEqual(importTrack.End, track.End); + Assert.AreEqual(importTrack.IsLinkedToPreviousTrack, track.IsLinkedToPreviousTrack); + Assert.AreEqual(importTrack.Position, track.Position); + Assert.AreEqual(importTrack.PostGap, track.PostGap); + Assert.AreEqual(importTrack.PreGap, track.PreGap); + Assert.AreEqual(importTrack.Flags, track.Flags); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track)), Times.Never); + } + + [TestMethod] + public void Clone_ImportTrackWithLength_ReturnsTrackCloneWithLengthSet() + { + // Arrange + var importTrack = new ImportTrack() + { + Artist = "Artist 1", + Title = "Title 1", + Begin = TimeSpan.Zero, + Length = new TimeSpan(0, 2, 54), + IsLinkedToPreviousTrack = true, + Position = 1, + PostGap = new TimeSpan(0, 0, 2), + PreGap = new TimeSpan(0, 0, 4), + Flags = [Flag.FourCH, Flag.DCP], + StartDateTime = new DateTime(2024, 2, 28, 19, 23, 54) + }; + // Act + var track = _trackManager.Clone(importTrack); + // Assert + Assert.AreEqual(importTrack.Artist, track.Artist); + Assert.AreEqual(importTrack.Title, track.Title); + Assert.AreEqual(importTrack.Begin, track.Begin); + Assert.AreEqual(importTrack.Length, track.End); + Assert.AreEqual(importTrack.IsLinkedToPreviousTrack, track.IsLinkedToPreviousTrack); + Assert.AreEqual(importTrack.Position, track.Position); + Assert.AreEqual(importTrack.PostGap, track.PostGap); + Assert.AreEqual(importTrack.PreGap, track.PreGap); + Assert.AreEqual(importTrack.Flags, track.Flags); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track)), Times.Never); + } + + [TestMethod] + public void CopyValues_AllDefaultValues_CopiesAllValues() + { + // Arrange + var source = new Track() + { + Artist = "Artist 1", + Title = "Title 1", + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 2, 54), + IsLinkedToPreviousTrack = true, + Position = 1, + PostGap = new TimeSpan(0, 0, 2), + PreGap = new TimeSpan(0, 0, 4), + Flags = [Flag.FourCH, Flag.DCP] + }; + var target = new Track(); + // Act + _trackManager.CopyValues(source, target); + // Assert + Assert.AreEqual(source.Artist, target.Artist); + Assert.AreEqual(source.Title, target.Title); + Assert.AreEqual(source.Begin, target.Begin); + Assert.AreEqual(source.End, target.End); + Assert.AreEqual(source.IsLinkedToPreviousTrack, target.IsLinkedToPreviousTrack); + Assert.AreEqual(source.Position, target.Position); + Assert.AreEqual(source.PostGap, target.PostGap); + Assert.AreEqual(source.PreGap, target.PreGap); + Assert.AreEqual(source.Flags, target.Flags); + Assert.AreEqual(source.Length, target.Length); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == target)), Times.Exactly(9)); + } + + [TestMethod] + public void CopyValues_ValuesSet_CopiesOnlySetValues() + { + // Arrange + var source = new Track() + { + Artist = "Artist 1", + Title = "Title 1", + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 2, 54), + IsLinkedToPreviousTrack = true, + Position = 1, + PostGap = new TimeSpan(0, 0, 2), + PreGap = new TimeSpan(0, 0, 4), + Flags = [Flag.FourCH, Flag.DCP] + }; + var target = new Track(); + // Act + _trackManager.CopyValues(source, target, setBegin: false, setPostGap:false, setLength: false, setFlags: false); + // Assert + Assert.AreEqual(source.Artist, target.Artist); + Assert.AreEqual(source.Title, target.Title); + Assert.AreNotEqual(source.Begin, target.Begin); + Assert.AreEqual(source.End, target.End); + Assert.AreEqual(source.IsLinkedToPreviousTrack, target.IsLinkedToPreviousTrack); + Assert.AreEqual(source.Position, target.Position); + Assert.AreNotEqual(source.PostGap, target.PostGap); + Assert.AreEqual(source.PreGap, target.PreGap); + Assert.AreNotEqual(source.Flags, target.Flags); + Assert.AreNotEqual(source.Length, target.Length); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == target)), Times.Exactly(6)); + } + + [TestMethod] + public void GetPreviousLinkedTrack_LinkedTracks_ReturnsTrack() + { + // Arrange + var track1 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 1 + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 2 + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetPreviousLinkedTrack(track2); + // Assert + Assert.IsNotNull(linkedTrack); + Assert.AreEqual(track1, linkedTrack); + } + + [TestMethod] + public void GetPreviousLinkedTrack_NotLinkedTracks_ReturnsNull() + { + // Arrange + var track1 = new Track() + { + Position = 1 + }; + var track2 = new Track() + { + Position = 2 + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetPreviousLinkedTrack(track2); + // Assert + Assert.IsNull(linkedTrack); + } + + [TestMethod] + public void GetPreviousLinkedTrack_WithoutPosition_ReturnsNull() + { + // Arrange + var track1 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 1 + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetPreviousLinkedTrack(track2); + // Assert + Assert.IsNull(linkedTrack); + } + + [TestMethod] + public void GetPreviousLinkedTrack_WithBegin_ReturnsTrack() + { + // Arrange + var track1 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 6, 37) + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true, + Begin = track1.End + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetPreviousLinkedTrack(track2); + // Assert + Assert.IsNotNull(linkedTrack); + Assert.AreEqual(track1, linkedTrack); + } + + [TestMethod] + public void GetNextLinkedTrack_LinkedTracks_ReturnsTrack() + { + // Arrange + var track1 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 1 + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 2 + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetNextLinkedTrack(track1); + // Assert + Assert.IsNotNull(linkedTrack); + Assert.AreEqual(track2, linkedTrack); + } + + [TestMethod] + public void GetNextLinkedTrack_NotLinkedTracks_ReturnsNull() + { + // Arrange + var track1 = new Track() + { + Position = 1 + }; + var track2 = new Track() + { + Position = 2 + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetNextLinkedTrack(track1); + // Assert + Assert.IsNull(linkedTrack); + } + + [TestMethod] + public void GetNextLinkedTrack_WithoutPosition_ReturnsNull() + { + // Arrange + var track1 = new Track() + { + IsLinkedToPreviousTrack = true + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 2 + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetNextLinkedTrack(track1); + // Assert + Assert.IsNull(linkedTrack); + } + + [TestMethod] + public void GetNextLinkedTrack_WithBegin_ReturnsTrack() + { + // Arrange + var track1 = new Track() + { + IsLinkedToPreviousTrack = true, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 8, 32) + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 2, + Begin = track1.End, + End = new TimeSpan(0, 13, 54) + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetNextLinkedTrack(track1); + // Assert + Assert.IsNotNull(linkedTrack); + Assert.AreEqual(track2, linkedTrack); + } + + [TestMethod] + public void GetNextLinkedTrack_OnlyOneTrack_ReturnsNull() + { + // Arrange + var track1 = new Track() + { + IsLinkedToPreviousTrack = true, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 8, 32) + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1] + }; + track1.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetNextLinkedTrack(track1); + // Assert + Assert.IsNull(linkedTrack); + } + + [TestMethod] + public void GetNextLinkedTrack_MultiplePositions_ReturnsTrack() + { + // Arrange + var track1 = new Track() + { + Position = 1, + IsLinkedToPreviousTrack = true, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 8, 32) + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 2, + Begin = track1.End, + End = new TimeSpan(0, 13, 54) + }; + var track3 = new Track() + { + IsLinkedToPreviousTrack = true, + Position = 2, + Begin = track2.End, + End = new TimeSpan(0, 17, 32) + }; + var cuesheet = new Cuesheet() + { + Tracks = [track1, track2, track3] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + track3.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetNextLinkedTrack(track1); + // Assert + Assert.IsNotNull(linkedTrack); + Assert.AreEqual(track2, linkedTrack); + } + + [TestMethod] + public void GetNextLinkedTrack_WithPosition_ReturnsTrack() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12) + }; + var track2 = new Track() + { + Begin = track1.End, + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + var linkedTrack = _trackManager.GetNextLinkedTrack(track1); + // Assert + Assert.IsNotNull(linkedTrack); + Assert.AreEqual(track2, linkedTrack); + } + + [TestMethod] + public void RecalculateLinkedTracksProperties_TrackHasNoBegin_SetsBegin() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12) + }; + var track2 = new Track() + { + Position = 2, + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + _trackManager.RecalculateLinkedTracksProperties(track2); + // Assert + Assert.AreEqual(track1.End, track2.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track2 && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Track.Begin))), Times.Once); + } + + [TestMethod] + public void RecalculateLinkedTracksProperties_TrackHasNoPosition_SetsPosition() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12) + }; + var track2 = new Track() + { + IsLinkedToPreviousTrack = true, + Begin = track1.End + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + _trackManager.RecalculateLinkedTracksProperties(track2); + // Assert + Assert.AreEqual((ushort?)2, track2.Position); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track2 && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Track.Position))), Times.Once); + } + + [TestMethod] + public void RecalculateLinkedTracksProperties_PreviousTrackHasNoEnd_SetsEnd() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + }; + var track2 = new Track() + { + Position = 2, + IsLinkedToPreviousTrack = true, + Begin = new TimeSpan(0, 3, 12) + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + _trackManager.RecalculateLinkedTracksProperties(track2); + // Assert + Assert.AreEqual(track2.Begin, track1.End); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track1 && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Track.End))), Times.Once); + } + + [TestMethod] + public void RecalculateLinkedTracksProperties_NextTrackNoPosition_SetsPosition() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12) + }; + var track2 = new Track() + { + Begin = track1.End, + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + _trackManager.RecalculateLinkedTracksProperties(track1); + // Assert + Assert.AreEqual((ushort?)2, track2.Position); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track2 && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Track.Position))), Times.Once); + } + + [TestMethod] + public void RecalculateLinkedTracksProperties_NextTrackNoBegin_SetsBegin() + { + // Arrange + var track1 = new Track() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 12) + }; + var track2 = new Track() + { + Position = 2, + IsLinkedToPreviousTrack = true + }; + var cuesheet = new Cuesheet() + { + Tracks = [track2, track1] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; + // Act + _trackManager.RecalculateLinkedTracksProperties(track1); + // Assert + Assert.AreEqual(track1.End, track2.Begin); + _traceChangeManager.Verify(x => x.AddChange(It.Is(y => y.TraceableObject == track2 && y.TraceableChange.PreviousValue == null && y.TraceableChange.PropertyName == nameof(Track.Begin))), Times.Once); + } + } +} diff --git a/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs b/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs index bcfd701d..13fbd775 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs @@ -63,14 +63,16 @@ public void CanGenerateExportfiles_ValidExtension_ReturnsSuccess() { Artist = "Test Artist", Title = "Test Title", - Audiofile = new Audiofile("Audio.mp3") + Audiofile = new Audiofile("Audio.mp3"), + Tracks = [ + new() + { + Position = 1, + Begin = TimeSpan.Zero, + End = new TimeSpan(0, 3, 43) + } + ] }; - cuesheet.AddTrack(new Track() - { - Position = 1, - Begin = TimeSpan.Zero, - End = new TimeSpan(0, 3, 43) - }); _sessionStateContainerMock.SetupProperty(x => x.Cuesheet, cuesheet); // Act @@ -85,27 +87,31 @@ public void GenerateExportfiles_WithoutSections_ReturnsExportFile() { // Arrange var filename = "Test valid Filename.cue"; - var cuesheet = new Cuesheet() - { - Artist = "Test artist cuesheet", - Title = "Test title cuesheet", - Audiofile = new Audiofile("Test audiofile.mp3") - }; - cuesheet.AddTrack(new Track() + var track1 = new Track() { Artist = "Test artist 1", Title = "Test title 1", Begin = TimeSpan.Zero, End = new TimeSpan(0, 4, 12), Position = 1 - }); - cuesheet.AddTrack(new Track() + }; + var track2 = new Track() { Artist = "Test artist 2", Title = "Test title 2", + Begin = track1.End, End = new TimeSpan(0, 8, 32), Position = 2 - }); + }; + var cuesheet = new Cuesheet() + { + Artist = "Test artist cuesheet", + Title = "Test title cuesheet", + Audiofile = new Audiofile("Test audiofile.mp3"), + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; _sessionStateContainerMock.SetupProperty(x => x.Cuesheet, cuesheet); // Act @@ -138,19 +144,19 @@ public void GenerateExportfiles_WithInvalidTracks_ReturnsFailure() { Artist = "Test Artist", Title = "Test Title", - Audiofile = new Audiofile("Audio.mp3") - }; - var track1 = new Track() - { - Position = 1, - Begin = TimeSpan.Zero, - }; - var track2 = new Track() - { - Position = 2, + Audiofile = new Audiofile("Audio.mp3"), + Tracks = [ + new() + { + Position = 1, + Begin = TimeSpan.Zero, + }, + new() + { + Position = 2, + } + ] }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); _sessionStateContainerMock.SetupProperty(x => x.Cuesheet, cuesheet); // Act diff --git a/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs b/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs index 2b57cc83..b3855d30 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs @@ -38,7 +38,7 @@ public ExportfileGeneratorTests() var mockLocalizer = new Mock>(); _exportfileGenerator = new ExportfileGenerator(_mockSessionStateContainer.Object, mockLocalizer.Object); } - + [TestMethod] public void GenerateExportFile_ShouldGenerateExportfile_WithoutSections() { @@ -50,27 +50,31 @@ public void GenerateExportFile_ShouldGenerateExportfile_WithoutSections() SchemeTracks = "%Track.Position% %Track.Artist% - %Track.Title%", Filename = "TestExport.txt" }; - var cuesheet = new Cuesheet() - { - Artist = "Test artist cuesheet", - Title = "Test title cuesheet", - Audiofile = new Audiofile("Test audiofile.mp3") - }; - cuesheet.AddTrack(new Track() + var track1 = new Track() { Artist = "Test artist 1", Title = "Test title 1", Begin = TimeSpan.Zero, End = new TimeSpan(0, 4, 12), Position = 1 - }); - cuesheet.AddTrack(new Track() + }; + var track2 = new Track() { Artist = "Test artist 2", Title = "Test title 2", + Begin = track1.End, End = new TimeSpan(0, 8, 32), Position = 2 - }); + }; + var cuesheet = new Cuesheet() + { + Artist = "Test artist cuesheet", + Title = "Test title cuesheet", + Audiofile = new Audiofile("Test audiofile.mp3"), + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; _mockSessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); // Act @@ -92,27 +96,31 @@ public void GenerateExportFile_ShouldHandleEmptyProfile() { // Arrange var exportProfile = new Exportprofile(); - var cuesheet = new Cuesheet() - { - Artist = "Test artist cuesheet", - Title = "Test title cuesheet", - Audiofile = new Audiofile("Test audiofile.mp3") - }; - cuesheet.AddTrack(new Track() + var track1 = new Track() { Artist = "Test artist 1", Title = "Test title 1", Begin = TimeSpan.Zero, End = new TimeSpan(0, 4, 12), Position = 1 - }); - cuesheet.AddTrack(new Track() + }; + var track2 = new Track() { Artist = "Test artist 2", Title = "Test title 2", + Begin = track1.End, End = new TimeSpan(0, 8, 32), Position = 2 - }); + }; + var cuesheet = new Cuesheet() + { + Artist = "Test artist cuesheet", + Title = "Test title cuesheet", + Audiofile = new Audiofile("Test audiofile.mp3"), + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; _mockSessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); // Act @@ -135,25 +143,28 @@ public void GenerateExportFile_ReturnsEmpty_WithInvalidTracks() SchemeTracks = "%Track.Position% %Track.Artist% - %Track.Title%", Filename = "TestExport.txt" }; - var cuesheet = new Cuesheet() - { - Artist = "Test artist cuesheet", - Title = "Test title cuesheet", - Audiofile = new Audiofile("Test audiofile.mp3") - }; - cuesheet.AddTrack(new Track() + var track1 = new Track() { Artist = "Test artist 1", Title = "Test title 1", Begin = TimeSpan.Zero, Position = 1 - }); - cuesheet.AddTrack(new Track() + }; + var track2 = new Track() { Artist = "Test artist 2", Title = "Test title 2", Position = 2 - }); + }; + var cuesheet = new Cuesheet() + { + Artist = "Test artist cuesheet", + Title = "Test title cuesheet", + Audiofile = new Audiofile("Test audiofile.mp3"), + Tracks = [track1, track2] + }; + track1.Cuesheet = cuesheet; + track2.Cuesheet = cuesheet; _mockSessionStateContainer.SetupProperty(x => x.Cuesheet, cuesheet); // Act diff --git a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs index 875a900b..66375390 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs @@ -19,19 +19,17 @@ using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; -using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Services.AudioCuesheet; using AudioCuesheetEditor.Services.IO; using AudioCuesheetEditor.Services.UI; -using Microsoft.AspNetCore.Components.Forms; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; -using System.Threading; +using System.Linq.Expressions; +using System.Reflection; using System.Threading.Tasks; namespace AudioCuesheetEditor.Tests.Services.IO @@ -44,15 +42,96 @@ public class ImportManagerTests private readonly Mock _traceChangeManagerMock; private readonly Mock _fileInputManagerMock; private readonly Mock _textImportServiceMock; - + private readonly Mock _trackManagerMock; public ImportManagerTests() { _traceChangeManagerMock = new(); _sessionStateContainerMock = new(); _fileInputManagerMock = new(); _textImportServiceMock = new(); + _trackManagerMock = new(); + _trackManagerMock.Setup(x => x.CopyValues(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((source, target, setIsLinkedToPreviousTrack, setPosition, setArtist, setTitle, setBegin, setEnd, setLength, setFlags, setPreGap, setPostGap) => + { + if (setIsLinkedToPreviousTrack) + { + TrackManager_SetValue(target, x => x.IsLinkedToPreviousTrack, source.IsLinkedToPreviousTrack, false); + } + if (setPosition) + { + TrackManager_SetValue(target, x => x.Position, source.Position, false); + } + if (setArtist) + { + TrackManager_SetValue(target, x => x.Artist, source.Artist, false); + } + if (setTitle) + { + TrackManager_SetValue(target, x => x.Title, source.Title, false); + } + if (setBegin) + { + TrackManager_SetValue(target, x => x.Begin, source.Begin, false); + } + if (setEnd) + { + TrackManager_SetValue(target, x => x.End, source.End, false); + } + if (setLength) + { + TrackManager_SetValue(target, x => x.Length, source.Length, false); + } + if (setFlags) + { + TrackManager_SetValue(target, x => x.Flags, source.Flags, false); + } + if (setPreGap) + { + TrackManager_SetValue(target, x => x.PreGap, source.PreGap, false); + } + if (setPostGap) + { + TrackManager_SetValue(target, x => x.PostGap, source.PostGap, false); + } + }); + _trackManagerMock.Setup(x => x.Clone(It.IsAny())).Returns(track => + { + var clone = new Track(); + Boolean setLength = true; + if (track.Begin.HasValue && track.End.HasValue) + { + setLength = false; + } + _trackManagerMock.Object.CopyValues(track, clone, setLength: setLength); + return clone; + }); var loggerMock = new Mock>(); - _service = new ImportManager(_sessionStateContainerMock.Object, _traceChangeManagerMock.Object, _fileInputManagerMock.Object, _textImportServiceMock.Object, loggerMock.Object); + _service = new ImportManager(_sessionStateContainerMock.Object, _traceChangeManagerMock.Object, _fileInputManagerMock.Object, _textImportServiceMock.Object, _trackManagerMock.Object, loggerMock.Object); + } + + void TrackManager_SetValue(Track track, Expression> propertyExpression, TProperty value, Boolean signalTraceChangeManager = true) + { + if (propertyExpression.Body is not MemberExpression memberExpression) + { + throw new ArgumentException("Expression must be a property"); + } + + if (memberExpression.Member is not PropertyInfo propertyInfo) + { + throw new ArgumentException("Member is not a property"); + } + + var previousValue = (TProperty?)propertyInfo.GetValue(track); + if (Equals(previousValue, value)) + { + return; + } + + propertyInfo.SetValue(track, value); + if (signalTraceChangeManager) + { + _traceChangeManagerMock.Object.AddChange(new(track, new(previousValue, propertyInfo.Name))); + } } [TestMethod()] @@ -112,8 +191,6 @@ public async Task AnalyseImportfile_WithTextfile_SetsImportCuesheet() Assert.AreEqual(importCuesheet.Tracks.First().Position, sessionStateContainerImportCuesheet.Tracks.First().Position); Assert.AreEqual(importCuesheet.Tracks.First().PreGap, sessionStateContainerImportCuesheet.Tracks.First().PreGap); Assert.AreEqual(importCuesheet.Tracks.First().PostGap, sessionStateContainerImportCuesheet.Tracks.First().PostGap); - _traceChangeManagerMock.Verify(x => x.TraceChanges(It.IsAny())); - _traceChangeManagerMock.Verify(x => x.TraceChanges(It.IsAny())); } [TestMethod()] @@ -140,74 +217,66 @@ public async Task AnalyseImportfile_WithoutAnalysedCuesheet_DoesNothing() } [TestMethod()] - public async Task AnalyseImportfile_ExistingCuesheet_OverwritesCuesheet() + public async Task AnalyseImportfile_ExistingCuesheet_SetsImportfileCuesheet() { // Arrange - var sessionStateContainerCuesheet = new Cuesheet() - { - Artist = "Test Cuesheet Artist", - Title = "Test Cuesheet Title", - Cataloguenumber = "Test Cuesheet Cataloguenumber" - }; - sessionStateContainerCuesheet.AddTrack(new() - { - Artist = "Test Track Artist 1", - Title = "Test Track Title 1", - Begin = new TimeSpan(0, 3, 20), - End = new TimeSpan(0, 7, 43), - Flags = [Flag.DCP], - Position = 1, - PreGap = new TimeSpan(0, 0, 2), - PostGap = new TimeSpan(0, 0, 4) - }); - var importCuesheet = new ImportCuesheet() - { - Artist = "Test ImportCuesheet Artist", - Title = "Test ImportCuesheet Title", - Audiofile = "Test ImportCuesheet Audiofile", - Cataloguenumber = "Test ImportCuesheet Cataloguenumber", - CDTextfile = "Test ImportCuesheet CDTextfile" - }; - importCuesheet.Tracks.Add(new() + var fileContent = @"PERFORMER ""Sample CD Artist"" +TITLE ""Sample CD Title"" +FILE ""Sample.mp3"" MP3 +TRACK 01 AUDIO + PERFORMER ""Sample Artist 1"" + TITLE ""Sample Title 1"" + INDEX 01 00:00:00 +TRACK 02 AUDIO + PERFORMER ""Sample Artist 2"" + TITLE ""Sample Title 2"" + INDEX 01 05:00:00 +TRACK 03 AUDIO + PERFORMER ""Sample Artist 3"" + TITLE ""Sample Title 3"" + INDEX 01 09:23:00 +TRACK 04 AUDIO + PERFORMER ""Sample Artist 4"" + TITLE ""Sample Title 4"" + INDEX 01 15:54:00 +TRACK 05 AUDIO + PERFORMER ""Sample Artist 5"" + TITLE ""Sample Title 5"" + INDEX 01 20:13:00 +TRACK 06 AUDIO + PERFORMER ""Sample Artist 6"" + TITLE ""Sample Title 6"" + INDEX 01 24:54:00 +TRACK 07 AUDIO + PERFORMER ""Sample Artist 7"" + TITLE ""Sample Title 7"" + INDEX 01 31:54:00 +TRACK 08 AUDIO + PERFORMER ""Sample Artist 8"" + TITLE ""Sample Title 8"" + INDEX 01 45:54:00 +"; + IImportfile sessionStateContainerImportFile = new Importfile() { - Artist = "Test ImportCuesheet Track Artist 1", - Title = "Test ImportCuesheet Track Title 1", - Begin = new TimeSpan(0, 0, 10), - End = new TimeSpan(0, 5, 23), - Flags = [Flag.SCMS], - Position = 1, - PreGap = new TimeSpan(0, 0, 4), - PostGap = new TimeSpan(0, 0, 6) - }); - var sessionStateContainerImportFile = new Importfile() - { - FileContent = "Some file content!", - FileType = ImportFileType.Cuesheet, - AnalyzedCuesheet = importCuesheet + FileContent = fileContent, + FileType = ImportFileType.Cuesheet }; - _sessionStateContainerMock.SetupGet(x => x.Cuesheet).Returns(() => sessionStateContainerCuesheet); - _sessionStateContainerMock.SetupSet(x => x.Cuesheet = It.IsAny()).Callback(cuesheet => sessionStateContainerCuesheet = cuesheet); _sessionStateContainerMock.SetupGet(x => x.Importfile).Returns(() => sessionStateContainerImportFile); + _sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(importfile => sessionStateContainerImportFile = importfile); // Act await _service.AnalyseImportfile(); // Assert - Assert.AreEqual(importCuesheet.Artist, sessionStateContainerCuesheet.Artist); - Assert.AreEqual(importCuesheet.Title, sessionStateContainerCuesheet.Title); - Assert.IsNotNull(sessionStateContainerCuesheet.Audiofile); - Assert.AreEqual(importCuesheet.Audiofile, sessionStateContainerCuesheet.Audiofile.Name); - Assert.IsNotNull(sessionStateContainerCuesheet.CDTextfile); - Assert.AreEqual(importCuesheet.CDTextfile, sessionStateContainerCuesheet.CDTextfile.Name); - Assert.HasCount(importCuesheet.Tracks.Count, sessionStateContainerCuesheet.Tracks); - Assert.AreEqual(importCuesheet.Tracks.First().Artist, sessionStateContainerCuesheet.Tracks.First().Artist); - Assert.AreEqual(importCuesheet.Tracks.First().Begin, sessionStateContainerCuesheet.Tracks.First().Begin); - Assert.AreEqual(importCuesheet.Tracks.First().End, sessionStateContainerCuesheet.Tracks.First().End); - CollectionAssert.AreEquivalent(importCuesheet.Tracks.First().Flags.ToList(), sessionStateContainerCuesheet.Tracks.First().Flags.ToList()); - Assert.AreEqual(importCuesheet.Tracks.First().Position, sessionStateContainerCuesheet.Tracks.First().Position); - Assert.AreEqual(importCuesheet.Tracks.First().PostGap, sessionStateContainerCuesheet.Tracks.First().PostGap); - Assert.AreEqual(importCuesheet.Tracks.First().PreGap, sessionStateContainerCuesheet.Tracks.First().PreGap); - Assert.AreEqual(importCuesheet.Tracks.First().Title, sessionStateContainerCuesheet.Tracks.First().Title); + Assert.IsNotNull(sessionStateContainerImportFile.AnalyzedCuesheet); + Assert.AreEqual("Sample CD Artist", sessionStateContainerImportFile.AnalyzedCuesheet.Artist); + Assert.AreEqual("Sample CD Title", sessionStateContainerImportFile.AnalyzedCuesheet.Title); + Assert.IsNotNull(sessionStateContainerImportFile.AnalyzedCuesheet.Audiofile); + Assert.AreEqual("Sample.mp3", sessionStateContainerImportFile.AnalyzedCuesheet.Audiofile); + Assert.HasCount(8, sessionStateContainerImportFile.AnalyzedCuesheet.Tracks); + Assert.AreEqual("Sample Artist 1", sessionStateContainerImportFile.AnalyzedCuesheet.Tracks.First().Artist); + Assert.AreEqual(TimeSpan.Zero, sessionStateContainerImportFile.AnalyzedCuesheet.Tracks.First().Begin); + Assert.AreEqual("Sample Title 1", sessionStateContainerImportFile.AnalyzedCuesheet.Tracks.First().Title); } [TestMethod] @@ -306,23 +375,26 @@ public async Task UploadFilesAsync_WithAudiofile_ImportsCorrectly() public void ImportCuesheet_WithImportCuesheetAvailable_ImportsCuesheetData() { // Arrange - var sessionStateContainerImportCuesheet = new Cuesheet() - { - Artist = "Artist 123", - Title = "Title 456" - }; - sessionStateContainerImportCuesheet.AddTrack(new() + var track1 = new Track() { Artist = "Track Artist 1", Title = "Track Title 1", End = new TimeSpan(0, 4, 23), - }); - sessionStateContainerImportCuesheet.AddTrack(new() + }; + var track2 = new Track() { Artist = "Track Artist 2", Title = "Track Title 2", End = new TimeSpan(0, 8, 54), - }); + }; + var sessionStateContainerImportCuesheet = new Cuesheet() + { + Artist = "Artist 123", + Title = "Title 456", + Tracks = [track1, track2] + }; + track1.Cuesheet = sessionStateContainerImportCuesheet; + track2.Cuesheet = sessionStateContainerImportCuesheet; _sessionStateContainerMock.SetupGet(x => x.ImportCuesheet).Returns(sessionStateContainerImportCuesheet); Cuesheet sessionStateContainerCuesheet = new(); _sessionStateContainerMock.SetupSet(x => x.Cuesheet = It.IsAny()).Callback(cuesheet => sessionStateContainerCuesheet = cuesheet); @@ -341,7 +413,7 @@ public void ImportCuesheet_WithImportCuesheetAvailable_ImportsCuesheetData() Assert.AreEqual(sessionStateContainerImportCuesheet.Tracks.Last().Title, sessionStateContainerCuesheet.Tracks.Last().Title); Assert.AreEqual(sessionStateContainerImportCuesheet.Tracks.Last().Begin, sessionStateContainerCuesheet.Tracks.Last().Begin); Assert.AreEqual(sessionStateContainerImportCuesheet.Tracks.Last().End, sessionStateContainerCuesheet.Tracks.Last().End); - _traceChangeManagerMock.Verify(x => x.RemoveTracedChanges(It.IsAny>())); + _traceChangeManagerMock.Verify(x => x.RemoveTracedChanges(It.IsAny>())); } [TestMethod] diff --git a/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs index 637db7bc..684ceb45 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs @@ -32,6 +32,15 @@ namespace AudioCuesheetEditor.Tests.Services.IO [TestClass()] public class TextImportServiceTests { + private readonly Mock _localStorageOptionsProviderMock; + private readonly TextImportService _textImportService; + + public TextImportServiceTests() + { + _localStorageOptionsProviderMock = new(); + _textImportService = new (_localStorageOptionsProviderMock.Object); + } + [TestMethod()] public async Task AnalyseAsync_SampleCuesheet_CreatesValidCuesheetAsync() { @@ -45,20 +54,19 @@ public async Task AnalyseAsync_SampleCuesheet_CreatesValidCuesheetAsync() Sample Artist 6 - Sample Title 6 00:31:54 Sample Artist 7 - Sample Title 7 00:45:54 Sample Artist 8 - Sample Title 8 01:15:54"; - var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions { SelectedImportProfile = ImportOptions.DefaultSelectedImportprofile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(importOptions); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(importOptions); var applicationOptions = new ApplicationOptions() { DefaultIsLinkedToPreviousTrack = true }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -91,15 +99,13 @@ public async Task AnalyseAsync_InvalidSchemeTracks_CreatesAnalyseExceptionAsync( SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'.{1,})", SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNotNull(importfile.AnalyseException); } @@ -123,17 +129,15 @@ public async Task AnalyseAsync_InputfileWithExtraSeperator_CreatesValidCuesheetA SchemeCuesheet = @"(?'Artist'\A.*)[|](?'Title'\w{1,})\t{1,}(?'CDTextfile'[^\r\n]+)", SchemeTracks = @"(?'Position'\d{1,})[|](?'Artist'.{1,}) - (?'Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'End'.{1,})" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importfile = await service.AnalyseAsync(fileContent); + var importfile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importfile.AnalyseException); Assert.IsNotNull(importfile.AnalyzedCuesheet); @@ -141,7 +145,7 @@ public async Task AnalyseAsync_InputfileWithExtraSeperator_CreatesValidCuesheetA Assert.AreEqual("CuesheetTitle", importfile.AnalyzedCuesheet.Title); Assert.AreEqual("c:\\tmp\\TestTextFile.cdt", importfile.AnalyzedCuesheet.CDTextfile); Assert.HasCount(8, importfile.AnalyzedCuesheet.Tracks); - Assert.AreEqual((uint)6, importfile.AnalyzedCuesheet.Tracks.ElementAt(5).Position); + Assert.AreEqual((ushort)6, importfile.AnalyzedCuesheet.Tracks.ElementAt(5).Position); Assert.AreEqual("Sample Artist 1", importfile.AnalyzedCuesheet.Tracks.ElementAt(0).Artist); Assert.AreEqual("Sample Title 1", importfile.AnalyzedCuesheet.Tracks.ElementAt(0).Title); Assert.AreEqual(new TimeSpan(0, 5, 0), importfile.AnalyzedCuesheet.Tracks.ElementAt(0).End); @@ -165,17 +169,15 @@ public async Task AnalyseAsync_InputfileWithSimplifiedScheme_CreatesValidCueshee SchemeCuesheet = @"Artist|Title CDTextfile", SchemeTracks = @"Position|Artist - Title End" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -183,7 +185,7 @@ public async Task AnalyseAsync_InputfileWithSimplifiedScheme_CreatesValidCueshee Assert.AreEqual("CuesheetTitle", importFile.AnalyzedCuesheet.Title); Assert.AreEqual("c:\\tmp\\TestTextFile.cdt", importFile.AnalyzedCuesheet.CDTextfile); Assert.HasCount(8, importFile.AnalyzedCuesheet.Tracks); - Assert.AreEqual((uint)6, importFile.AnalyzedCuesheet.Tracks.ElementAt(5).Position); + Assert.AreEqual((ushort)6, importFile.AnalyzedCuesheet.Tracks.ElementAt(5).Position); Assert.AreEqual("Sample Artist 1", importFile.AnalyzedCuesheet.Tracks.ElementAt(0).Artist); Assert.AreEqual("Sample Title 1", importFile.AnalyzedCuesheet.Tracks.ElementAt(0).Title); Assert.AreEqual(new TimeSpan(0, 5, 0), importFile.AnalyzedCuesheet.Tracks.ElementAt(0).End); @@ -217,17 +219,15 @@ public async Task AnalyseAsync_InvalidScheme_CreatesAnalyseExceptionAsync() SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cuesheet.Cataloguenumber'.{1,})", SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNotNull(importFile.AnalyseException); } @@ -260,17 +260,15 @@ public async Task AnalyseAsync_CuesheetWithTextfileAndCatalogueNumber_CreatesVal SchemeCuesheet = @"(?'Artist'\A.*)[|](?'Title'\w{1,})\t{1,}(?'CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cataloguenumber'[^\r\n]+)", SchemeTracks = @"(?'Position'.{1,})[|](?'Artist'.{1,}) - (?'Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'End'.{1,})" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -279,7 +277,7 @@ public async Task AnalyseAsync_CuesheetWithTextfileAndCatalogueNumber_CreatesVal Assert.AreEqual("c:\\tmp\\TestTextFile.cdt", importFile.AnalyzedCuesheet.CDTextfile); Assert.AreEqual("A83412346734", importFile.AnalyzedCuesheet.Cataloguenumber); Assert.HasCount(8, importFile.AnalyzedCuesheet.Tracks); - Assert.AreEqual((uint)6, importFile.AnalyzedCuesheet.Tracks.ElementAt(5).Position); + Assert.AreEqual((ushort)6, importFile.AnalyzedCuesheet.Tracks.ElementAt(5).Position); Assert.AreEqual("Sample Artist 1", importFile.AnalyzedCuesheet.Tracks.ElementAt(0).Artist); Assert.AreEqual("Sample Title 1", importFile.AnalyzedCuesheet.Tracks.ElementAt(0).Title); Assert.AreEqual(new TimeSpan(0, 5, 0), importFile.AnalyzedCuesheet.Tracks.ElementAt(0).End); @@ -302,17 +300,15 @@ public async Task AnalyseAsync_CuesheetTracksOnly_CreatesValidCuesheetAsync() { SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -332,17 +328,15 @@ public async Task AnalyseAsync_CuesheetBug213_CreatesValidCuesheetAsync() SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}", TimeSpanFormat = new() { Scheme = $"{nameof(TimeSpanFormat.Minutes)}:{nameof(TimeSpanFormat.Seconds)}" } }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -367,17 +361,15 @@ public async Task AnalyseAsync_CuesheetWithFlags_CreatesValidCuesheetAsync() UseRegularExpression = true, SchemeTracks = "(?'Artist'[a-zA-Z0-9_ .();äöü&:,]+) - (?'Title'[a-zA-Z0-9_ .();äöü]+)[\t ]+(?'End'[0-9]{2}:[0-9]{2}:[0-9]{2})(?:[\t ]+(?'Flags'[A-Za-z0-9 ,]+))?(?=[\t ]*(?:\r?\n|$))" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -412,17 +404,15 @@ public async Task AnalyseAsync_CuesheetWithPreGapAndPostGap_CreatesValidCuesheet UseRegularExpression = true, SchemeTracks = "(?'Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'PreGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'PostGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -462,17 +452,15 @@ public async Task AnalyseAsync_SchemeCuesheetOnly_CreatesFileContentRecognizedOn { SchemeCuesheet = $"{nameof(ImportCuesheet.Artist)} - {nameof(ImportCuesheet.Title)}\t{nameof(ImportCuesheet.Audiofile)}" }; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -509,17 +497,15 @@ public async Task AnalyseAsync_CuesheetWithoutTracks_CreatesValidFileContentReco Sample Artist 6 - Sample Title 6 00:31:54 Sample Artist 7 - Sample Title 7 00:45:54 Sample Artist 8 - Sample Title 8 01:15:54"; - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = ImportOptions.DefaultSelectedImportprofile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -544,17 +530,15 @@ public async Task AnalyseAsync_TextfileBug233_CreatesValidFileContentRecognizedA SchemeTracks = $"{nameof(ImportTrack.Artist)} - {nameof(ImportTrack.Title)}\t{nameof(ImportTrack.End)}" }; var fileContent = File.ReadAllText("Resources/Textimport_Bug_#233.txt"); - var localStorageOptionsProviderMock = new Mock(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -576,17 +560,15 @@ public async Task AnalyseAsync_WithRegularExpression_ReturnsCuesheetAsync() SchemeTracks = "\\s*\\d+\\s*(?.*?)\\s*(?.*?)</td>\\s*<td>(?<StartDateTime>.*?)</td>\\s*</tr>" }; var fileContent = File.ReadAllText("Resources/Traktor Export.html"); - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.AreEqual(fileContent, importFile.FileContent); Assert.IsNull(importFile.AnalyseException); @@ -616,17 +598,15 @@ public async Task AnalyseAsync_WithoutRegularExpression_ReturnsCuesheetAsync() SchemeTracks = "Artist - Title\tBegin" }; var fileContent = File.ReadAllText("Resources/Textimport with Cuesheetdata.txt"); - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -689,17 +669,15 @@ public async Task AnalyseAsync_TextfileWithStartDateTime_CreatesValidCuesheetAsy UseRegularExpression = false, SchemeTracks = $"{nameof(ImportTrack.Artist)}~{nameof(ImportTrack.Title)}~{nameof(ImportTrack.StartDateTime)}" }; - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); @@ -726,17 +704,15 @@ public async Task AnalyseAsync_WithCommonDataMatchingMultipleLines_SetsCommonDat SchemeTracks = "Artist - Title\tBegin" }; var fileContent = File.ReadAllText("Resources/Sample_Inputfile.txt"); - var localStorageOptionsProviderMock = new Mock<ILocalStorageOptionsProvider>(); var options = new ImportOptions { SelectedImportProfile = profile }; - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ImportOptions>()).ReturnsAsync(options); var applicationOptions = new ApplicationOptions(); - localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); - var service = new TextImportService(localStorageOptionsProviderMock.Object); + _localStorageOptionsProviderMock.Setup(x => x.GetOptionsAsync<ApplicationOptions>()).ReturnsAsync(applicationOptions); // Act - var importFile = await service.AnalyseAsync(fileContent); + var importFile = await _textImportService.AnalyseAsync(fileContent); // Assert Assert.IsNull(importFile.AnalyseException); Assert.IsNotNull(importFile.AnalyzedCuesheet); diff --git a/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs b/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs index b7e8ef56..a8205c0f 100644 --- a/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs +++ b/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs @@ -16,6 +16,7 @@ using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.Utility; using AudioCuesheetEditor.Services.UI; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -109,5 +110,67 @@ public void GetTimespanFormatted_InvalidFormat_FallbackToDefault() // Assert Assert.AreEqual("01:30:27.2001030", result); } + + [TestMethod()] + public async Task GetTimespanAsync_CustomValidFormat_ReturnsTimeSpanAsync() + { + // Arrange + var options = new ApplicationOptions() + { + TimeSpanFormat = new() + { + Scheme = $"{TimeSpanFormat.Hours}-{TimeSpanFormat.Minutes}-{TimeSpanFormat.Seconds}" + } + }; + var mockOptionsProvider = new Mock<ILocalStorageOptionsProvider>(); + mockOptionsProvider + .Setup(p => p.GetOptionsAsync<ApplicationOptions>()) + .ReturnsAsync(options); + var parser = new ApplicationOptionsTimeSpanParser(mockOptionsProvider.Object); + // Act + var result = await parser.GetTimespanAsync("23-45-56"); + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(new TimeSpan(23, 45, 56), result); + } + + [TestMethod()] + public async Task GetTimespanAsync_CustomInvalidFormat_ReturnsNullAsync() + { + // Arrange + var options = new ApplicationOptions() + { + TimeSpanFormat = new() + { + Scheme = $"INVALID_FORMAT" + } + }; + var mockOptionsProvider = new Mock<ILocalStorageOptionsProvider>(); + mockOptionsProvider + .Setup(p => p.GetOptionsAsync<ApplicationOptions>()) + .ReturnsAsync(options); + var parser = new ApplicationOptionsTimeSpanParser(mockOptionsProvider.Object); + // Act + var result = await parser.GetTimespanAsync("23-45-56"); + // Assert + Assert.IsNull(result); + } + + [TestMethod()] + public async Task GetTimespanAsync_DefaultFormat_ReturnsTimeSpanAsync() + { + // Arrange + var options = new ApplicationOptions(); + var mockOptionsProvider = new Mock<ILocalStorageOptionsProvider>(); + mockOptionsProvider + .Setup(p => p.GetOptionsAsync<ApplicationOptions>()) + .ReturnsAsync(options); + var parser = new ApplicationOptionsTimeSpanParser(mockOptionsProvider.Object); + // Act + var result = await parser.GetTimespanAsync("23:45:56"); + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(new TimeSpan(23, 45, 56), result); + } } } \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs b/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs index 41606bdf..6a2cfe22 100644 --- a/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs @@ -13,25 +13,28 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Services.UI; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using System; +using System.Threading.Tasks; namespace AudioCuesheetEditor.Tests.Services.UI { [TestClass] public class SessionStateContainerTests { - private readonly Mock<ITraceChangeManager> traceChangeManagerMock; - private readonly SessionStateContainer sessionStateContainer; + private readonly SessionStateContainer _sessionStateContainer; + private readonly Mock<ILocalStorageOptionsProvider> _localStorageOptionsProvider; public SessionStateContainerTests() { - traceChangeManagerMock = new Mock<ITraceChangeManager>(); - sessionStateContainer = new SessionStateContainer(traceChangeManagerMock.Object); + _localStorageOptionsProvider = new(); + _sessionStateContainer = new(_localStorageOptionsProvider.Object); } [TestMethod] @@ -40,17 +43,13 @@ public void Cuesheet_SetNewValue_ShouldTriggerCuesheetChangedEvent() // Arrange var newCuesheet = new Cuesheet(); bool eventTriggered = false; - bool traceablePropertyChangedFired = false; - sessionStateContainer.CuesheetChanged += (sender, args) => eventTriggered = true; - sessionStateContainer.TraceablePropertyChanged += (sender, args) => traceablePropertyChangedFired = true; + _sessionStateContainer.CuesheetChanged += (sender, args) => eventTriggered = true; // Act - sessionStateContainer.Cuesheet = newCuesheet; + _sessionStateContainer.Cuesheet = newCuesheet; // Assert Assert.IsTrue(eventTriggered); - Assert.IsTrue(traceablePropertyChangedFired); - traceChangeManagerMock.Verify(m => m.TraceChanges(newCuesheet), Times.Once); } [TestMethod] @@ -59,47 +58,93 @@ public void ImportCuesheet_SetNewValue_ShouldTriggerImportCuesheetChangedEvent() // Arrange var newImportCuesheet = new Cuesheet(); bool eventTriggered = false; - sessionStateContainer.ImportCuesheetChanged += (sender, args) => eventTriggered = true; + _sessionStateContainer.ImportCuesheetChanged += (sender, args) => eventTriggered = true; // Act - sessionStateContainer.ImportCuesheet = newImportCuesheet; + _sessionStateContainer.ImportCuesheet = newImportCuesheet; // Assert Assert.IsTrue(eventTriggered); } [TestMethod] - public void ImportAudiofile_SetNewValue_ShouldUpdateImportCuesheetAndTriggerEvent() + public void ResetImport_ShouldClearImportProperties() { // Arrange - var importCuesheet = new Cuesheet(); - var audioFile = new Audiofile("Test audio file.mp3"); - sessionStateContainer.ImportCuesheet = importCuesheet; - bool eventTriggered = false; - sessionStateContainer.ImportCuesheetChanged += (sender, args) => eventTriggered = true; + _sessionStateContainer.Importfile = Mock.Of<IImportfile>(); + _sessionStateContainer.ImportCuesheet = new Cuesheet(); // Act - sessionStateContainer.ImportAudiofile = audioFile; + _sessionStateContainer.ResetImport(); // Assert - Assert.AreEqual(audioFile, importCuesheet.Audiofile); - Assert.IsTrue(eventTriggered); + Assert.IsNull(_sessionStateContainer.Importfile); + Assert.IsNull(_sessionStateContainer.ImportAudiofile); + Assert.IsNull(_sessionStateContainer.ImportCuesheet); } [TestMethod] - public void ResetImport_ShouldClearImportProperties() + public async Task InitializeAsync_NotInitialized_ShouldInitializeAsync() + { + // Arrange + var viewOptions = new ViewOptions(); + _localStorageOptionsProvider.Setup(x => x.GetOptionsAsync<ViewOptions>()).ReturnsAsync(viewOptions); + // Act + await _sessionStateContainer.InitializeAsync(); + // Assert + _localStorageOptionsProvider.Verify(x => x.GetOptionsAsync<ViewOptions>(), Times.Once); + } + + [TestMethod] + public async Task InitializeAsync_Initialized_ShouldNoptInitializeAsync() + { + // Arrange + var viewOptions = new ViewOptions(); + _localStorageOptionsProvider.Setup(x => x.GetOptionsAsync<ViewOptions>()).ReturnsAsync(viewOptions); + await _sessionStateContainer.InitializeAsync(); + // Act + await _sessionStateContainer.InitializeAsync(); + // Assert + _localStorageOptionsProvider.Verify(x => x.GetOptionsAsync<ViewOptions>(), Times.Once); + } + + [TestMethod] + public async Task GetActiveCuesheet_Initialized_ReturnsDetailViewCuesheeetAsync() { // Arrange - sessionStateContainer.Importfile = Mock.Of<IImportfile>(); - sessionStateContainer.ImportCuesheet = new Cuesheet(); + var viewOptions = new ViewOptions(); + _localStorageOptionsProvider.Setup(x => x.GetOptionsAsync<ViewOptions>()).ReturnsAsync(viewOptions); + await _sessionStateContainer.InitializeAsync(); + // Act + var result = _sessionStateContainer.GetActiveCuesheet(); + // Assert + Assert.AreEqual(_sessionStateContainer.Cuesheet, result); + } + [TestMethod] + public async Task GetActiveCuesheet_InitializedWithImportView_ReturnsDetailViewCuesheeetAsync() + { + // Arrange + var viewOptions = new ViewOptions() + { + ActiveTab = ViewMode.ImportView + }; + _localStorageOptionsProvider.Setup(x => x.GetOptionsAsync<ViewOptions>()).ReturnsAsync(viewOptions); + await _sessionStateContainer.InitializeAsync(); // Act - sessionStateContainer.ResetImport(); + var result = _sessionStateContainer.GetActiveCuesheet(); + // Assert + Assert.AreEqual(_sessionStateContainer.ImportCuesheet, result); + } + [TestMethod] + public void GetActiveCuesheet_NotInitialized_ThrowsException() + { + // Arrange + // Act + var exception = Assert.Throws<InvalidOperationException>(() => _sessionStateContainer.GetActiveCuesheet()); // Assert - Assert.IsNull(sessionStateContainer.Importfile); - Assert.IsNull(sessionStateContainer.ImportAudiofile); - Assert.IsNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual("Not initialized!", exception.Message); } } } \ No newline at end of file diff --git a/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs b/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs index 195cbcb3..98b99a17 100644 --- a/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/UI/TraceChangeManagerTests.cs @@ -14,12 +14,10 @@ //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Model.Entity; using AudioCuesheetEditor.Services.UI; using AudioCuesheetEditor.Tests.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Collections.Generic; using System.Linq; namespace AudioCuesheetEditor.Tests.Services.UI @@ -27,386 +25,331 @@ namespace AudioCuesheetEditor.Tests.Services.UI [TestClass()] public class TraceChangeManagerTests { - private static T CallInItsOwnScope<T>(Func<T> getter) - { - return getter(); - } + private readonly TraceChangeManager _traceChangeManager = new(TestHelper.CreateLogger<TraceChangeManager>()); [TestMethod()] - public void TraceChangesTest() + public void AddChange_SingleChange_EnablesUndo() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var callResult = CallInItsOwnScope(() => + // Arrange + var cuesheet = new Cuesheet { - var track = new Track(); - manager.TraceChanges(track); - var track2 = new Track(); - manager.TraceChanges(track2); - track2.Title = "Title 2"; - track.Artist = "Artist 1"; - track = null; - GC.Collect(); - return track2; - }); - GC.Collect(); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - Assert.IsNull(callResult.Title); - Assert.IsFalse(manager.CanUndo); + Artist = "Test Artist 1" + }; + var tracedObjectHistoryChangedFired = false; + _traceChangeManager.TracedObjectHistoryChanged += delegate + { + tracedObjectHistoryChangedFired = true; + }; + // Act + _traceChangeManager.AddChange(new(cuesheet, new(null, nameof(Cuesheet.Artist)))); + // Assert + Assert.IsTrue(_traceChangeManager.CanUndo); + Assert.IsFalse(_traceChangeManager.CanRedo); + Assert.IsTrue(tracedObjectHistoryChangedFired); } [TestMethod()] - public void UndoTest() + public void AddChange_BulkChange_EnablesUndo() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var track = new Track(); - manager.TraceChanges(track); - Assert.IsFalse(manager.CanUndo); - manager.Undo(); - track.Position = 3; - Assert.IsTrue(manager.CanUndo); - track.Artist = "Artist"; - track.End = new TimeSpan(0, 3, 23); - manager.Undo(); - Assert.IsTrue(manager.CanRedo); - Assert.IsNull(track.End); - var track2 = new Track(); - manager.TraceChanges(track2); - track2.Position = 5; - Assert.IsFalse(manager.CanRedo); - track2.Artist = "Artist 2"; - track2.Title = "Title 2"; - track2.Length = new TimeSpan(0, 4, 12); - Assert.AreEqual(new TimeSpan(0, 4, 12), track2.Length); - manager.Undo(); - Assert.IsNull(track2.Length); + // Arrange + var cuesheet = new Cuesheet + { + Artist = "Test Artist 1", + Title = "Test Title 1", + Cataloguenumber = "1234567890" + }; + var tracedObjectHistoryChangedFired = false; + _traceChangeManager.TracedObjectHistoryChanged += delegate + { + tracedObjectHistoryChangedFired = true; + }; + _traceChangeManager.BulkEdit = true; + _traceChangeManager.AddChange(new(cuesheet, new(null, nameof(Cuesheet.Artist)))); + _traceChangeManager.AddChange(new(cuesheet, new(null, nameof(Cuesheet.Title)))); + _traceChangeManager.AddChange(new(cuesheet, new(null, nameof(Cuesheet.Cataloguenumber)))); + // Act + _traceChangeManager.BulkEdit = false; + // Assert + Assert.IsTrue(_traceChangeManager.CanUndo); + Assert.IsFalse(_traceChangeManager.CanRedo); + Assert.IsTrue(tracedObjectHistoryChangedFired); } [TestMethod()] - public void RedoTest() + public void BulkEdit_WithoutChange_DoesntAddChange() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var track = new Track(); - manager.TraceChanges(track); - Assert.IsFalse(manager.CanUndo); - track.Position = 3; - Assert.IsTrue(manager.CanUndo); - track.Artist = "Artist"; - track.End = new TimeSpan(0, 3, 23); - Assert.IsFalse(manager.CanRedo); - manager.Redo(); - manager.Undo(); - Assert.IsTrue(manager.CanRedo); - Assert.IsNull(track.End); - manager.Redo(); - Assert.AreEqual(new TimeSpan(0, 3, 23), track.End); - var track2 = new Track(); - manager.TraceChanges(track2); - track2.Position = 5; - Assert.IsFalse(manager.CanRedo); - track2.Artist = "Artist 2"; - track2.Title = "Title 2"; - track2.Length = new TimeSpan(0, 4, 12); - Assert.AreEqual(new TimeSpan(0, 4, 12), track2.Length); - manager.Undo(); - Assert.IsNull(track2.Length); - manager.Redo(); - Assert.AreEqual(new TimeSpan(0, 4, 12), track2.Length); + // Arrange + _traceChangeManager.BulkEdit = true; + // Act + _traceChangeManager.BulkEdit = false; + // Assert + Assert.IsFalse(_traceChangeManager.CanUndo); } [TestMethod()] - public void UndoRedoCombinationTest() + public void BulkEdit_AlreadyEnabledBulkEdit_DoesntDropPreviousEdits() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var track = new Track(); - manager.TraceChanges(track); - Assert.IsFalse(manager.CanUndo); - track.Position = 3; - Assert.IsTrue(manager.CanUndo); - track.Artist = "Artist"; - track.End = new TimeSpan(0, 3, 23); - Assert.IsFalse(manager.CanRedo); - manager.Redo(); - manager.Undo(); - Assert.IsTrue(manager.CanRedo); - Assert.IsNull(track.End); - manager.Redo(); - Assert.AreEqual(new TimeSpan(0, 3, 23), track.End); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - Assert.IsNull(track.End); - manager.Redo(); - Assert.AreEqual(new TimeSpan(0, 3, 23), track.End); + // Arrange + var cuesheet = new Cuesheet + { + Artist = "Test Artist 1", + Title = "Test Title 1", + Cataloguenumber = "1234567890" + }; + _traceChangeManager.BulkEdit = true; + _traceChangeManager.AddChange(new(cuesheet, new(null, nameof(Cuesheet.Artist)))); + _traceChangeManager.AddChange(new(cuesheet, new(null, nameof(Cuesheet.Title)))); + _traceChangeManager.AddChange(new(cuesheet, new(null, nameof(Cuesheet.Cataloguenumber)))); + _traceChangeManager.BulkEdit = true; + _traceChangeManager.BulkEdit = false; + // Act + _traceChangeManager.Undo(); + // Assert + Assert.IsNull(cuesheet.Artist); + Assert.IsNull(cuesheet.Title); + Assert.IsNull(cuesheet.Cataloguenumber); + Assert.IsTrue(_traceChangeManager.CanRedo); } [TestMethod()] - public void TrackListTest() + public void Undo_SingleChange_UndoesChange() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var cuesheet = new Cuesheet(); - manager.TraceChanges(cuesheet); - Assert.IsFalse(manager.CanUndo); - cuesheet.AddTrack(new Track()); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - Assert.IsEmpty(cuesheet.Tracks); - Assert.IsTrue(manager.CanRedo); - manager.Redo(); - Assert.HasCount(1, cuesheet.Tracks); - Assert.IsFalse(manager.CanRedo); + // Arrange + var track = new Track + { + Position = 3 + }; + var undoDoneEventFired = false; + _traceChangeManager.UndoDone += delegate + { + undoDoneEventFired = true; + }; + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Position)))); + // Act + _traceChangeManager.Undo(); + // Assert + Assert.IsNull(track.Position); + Assert.IsTrue(undoDoneEventFired); + Assert.IsFalse(_traceChangeManager.CanUndo); + Assert.IsTrue(_traceChangeManager.CanRedo); } [TestMethod()] - public void RemoveTracksTest() + public void Undo_BulkChange_UndoesChanges() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var cuesheet = new Cuesheet(); - manager.TraceChanges(cuesheet); - var track1 = new Track() + // Arrange + var track = new Track { - Artist = "Track 1 Artist", - Title = "Track 1 Title", - End = new TimeSpan(0, 2, 30) + Position = 3, + Artist = "Artist" }; - var track2 = new Track() + var undoDoneEventFired = false; + _traceChangeManager.UndoDone += delegate { - Artist = "Track 2 Artist", - Title = "Track 2 Title", - End = new TimeSpan(0, 4, 20) + undoDoneEventFired = true; }; - var track3 = new Track() + _traceChangeManager.BulkEdit = true; + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Position)))); + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Artist)))); + _traceChangeManager.BulkEdit = false; + // Act + _traceChangeManager.Undo(); + // Assert + Assert.IsNull(track.Position); + Assert.IsNull(track.Artist); + Assert.IsTrue(undoDoneEventFired); + Assert.IsFalse(_traceChangeManager.CanUndo); + Assert.IsTrue(_traceChangeManager.CanRedo); + } + + [TestMethod()] + public void Redo_SingleChange_UndoesChange() + { + // Arrange + var track = new Track { - Artist = "Track 3 Artist", - Title = "Track 3 Title", - End = new TimeSpan(0, 7, 12) + Position = 3 }; - var track4 = new Track() + var redoDoneEventFired = false; + _traceChangeManager.RedoDone += delegate { - Artist = "Track 4 Artist", - Title = "Track 4 Title", - End = new TimeSpan(0, 9, 12) + redoDoneEventFired = true; }; - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - cuesheet.AddTrack(track4); - Assert.AreEqual(ValidationStatus.Success, track1.Validate().Status); - Assert.AreEqual(ValidationStatus.Success, track2.Validate().Status); - Assert.AreEqual(ValidationStatus.Success, track3.Validate().Status); - Assert.AreEqual(ValidationStatus.Success, track4.Validate().Status); - var tracksToRemove = new List<Track> + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Position)))); + _traceChangeManager.Undo(); + // Act + _traceChangeManager.Redo(); + // Assert + Assert.AreEqual((ushort)3, track.Position); + Assert.IsTrue(redoDoneEventFired); + Assert.IsTrue(_traceChangeManager.CanUndo); + Assert.IsFalse(_traceChangeManager.CanRedo); + } + + [TestMethod()] + public void Redo_BulkChange_UndoesChanges() + { + // Arrange + var track = new Track { - track2, - track4 + Position = 3, + Artist = "Artist" }; - cuesheet.RemoveTracks(tracksToRemove); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - Assert.HasCount(4, cuesheet.Tracks); - //We need to set object references back to cue sheet tracks since TraceChangeManager creates new objects - track1 = cuesheet.Tracks.ElementAt(0); - track2 = cuesheet.Tracks.ElementAt(1); - track3 = cuesheet.Tracks.ElementAt(2); - track4 = cuesheet.Tracks.ElementAt(3); - Assert.AreEqual(ValidationStatus.Success, track1.Validate().Status); - Assert.AreEqual(ValidationStatus.Success, track2.Validate().Status); - Assert.AreEqual(ValidationStatus.Success, track3.Validate().Status); - Assert.AreEqual(ValidationStatus.Success, track4.Validate().Status); + var redoDoneEventFired = false; + _traceChangeManager.RedoDone += delegate + { + redoDoneEventFired = true; + }; + _traceChangeManager.BulkEdit = true; + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Position)))); + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Artist)))); + _traceChangeManager.BulkEdit = false; + _traceChangeManager.Undo(); + // Act + _traceChangeManager.Redo(); + // Assert + Assert.AreEqual((ushort)3, track.Position); + Assert.AreEqual("Artist", track.Artist); + Assert.IsTrue(redoDoneEventFired); + Assert.IsTrue(_traceChangeManager.CanUndo); + Assert.IsFalse(_traceChangeManager.CanRedo); } - [TestMethod()] - public void ResetTest() + [TestMethod] + public void Undo_ObjectReferences_UndoesChanges() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - Assert.IsFalse(manager.CanUndo); - Assert.IsFalse(manager.CanRedo); - var cuesheet = new Cuesheet(); - manager.TraceChanges(cuesheet); - var track1 = new Track() + // Arrange + var cuesheet = new Cuesheet() { - Artist = "Track 1 Artist", - Title = "Track 1 Title", - End = new TimeSpan(0, 2, 30) + Tracks = [new()] + }; + var undoDoneEventFired = false; + _traceChangeManager.UndoDone += delegate + { + undoDoneEventFired = true; }; - cuesheet.AddTrack(track1); - Assert.IsTrue(manager.CanUndo); - manager.Reset(); - Assert.IsFalse(manager.CanUndo); - Assert.IsFalse(manager.CanRedo); + _traceChangeManager.AddChange(new(cuesheet, new(Enumerable.Empty<Track>(), nameof(Cuesheet.Tracks)))); + // Act + _traceChangeManager.Undo(); + // Assert + Assert.IsEmpty(cuesheet.Tracks); + Assert.IsFalse(_traceChangeManager.CanUndo); + Assert.IsTrue(_traceChangeManager.CanRedo); + Assert.IsTrue(undoDoneEventFired); } - [TestMethod()] - public void BulkEditTracksTest() + [TestMethod] + public void Redo_ObjectReferences_UndoesChanges() { - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var cuesheet = new Cuesheet(); - manager.TraceChanges(cuesheet); - var tracedObjectHistoryChangedFired = false; - manager.TracedObjectHistoryChanged += delegate + // Arrange + var cuesheet = new Cuesheet() { - tracedObjectHistoryChangedFired = true; + Tracks = [new()] }; - var track1 = new Track(); - var track2 = new Track(); - var track3 = new Track(); - var track4 = new Track(); - manager.TraceChanges(track1); - manager.TraceChanges(track2); - manager.TraceChanges(track3); - manager.TraceChanges(track4); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - cuesheet.AddTrack(track4); - track1.IsLinkedToPreviousTrack = true; - track2.IsLinkedToPreviousTrack = true; - track3.IsLinkedToPreviousTrack = true; - track4.IsLinkedToPreviousTrack = true; - var editedTrack = new Track() { Artist = "Test", Title = "Testtitle", Length = new TimeSpan(0, 2, 0) }; - manager.BulkEdit = true; - track1.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - track2.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - track3.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - track4.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - manager.BulkEdit = false; - Assert.IsTrue(manager.CanUndo); - Assert.AreEqual(editedTrack.Length, track1.End); - Assert.AreEqual(track1.End, track2.Begin); - Assert.AreEqual(track2.End, track3.Begin); - Assert.AreEqual(track3.End, track4.Begin); - Assert.IsNotNull(track4.End); - Assert.AreEqual(editedTrack.Artist, track1.Artist); - Assert.AreEqual(editedTrack.Artist, track2.Artist); - Assert.AreEqual(editedTrack.Artist, track3.Artist); - Assert.AreEqual(editedTrack.Artist, track4.Artist); - Assert.AreEqual(editedTrack.Title, track1.Title); - Assert.AreEqual(editedTrack.Title, track2.Title); - Assert.AreEqual(editedTrack.Title, track3.Title); - Assert.AreEqual(editedTrack.Title, track4.Title); - Assert.IsTrue(tracedObjectHistoryChangedFired); - manager.Undo(); - Assert.IsNull(track1.End); - Assert.IsNull(track2.End); - Assert.IsNull(track3.End); - Assert.IsNull(track4.End); - Assert.IsNull(track1.Artist); - Assert.IsNull(track2.Artist); - Assert.IsNull(track3.Artist); - Assert.IsNull(track4.Artist); - Assert.IsNull(track1.Title); - Assert.IsNull(track2.Title); - Assert.IsNull(track3.Title); - Assert.IsNull(track4.Title); + var redoDoneEventFired = false; + _traceChangeManager.RedoDone += delegate + { + redoDoneEventFired = true; + }; + _traceChangeManager.AddChange(new(cuesheet, new(Enumerable.Empty<Track>(), nameof(Cuesheet.Tracks)))); + _traceChangeManager.Undo(); + // Act + _traceChangeManager.Redo(); + // Assert + Assert.HasCount(1, cuesheet.Tracks); + Assert.IsTrue(_traceChangeManager.CanUndo); + Assert.IsFalse(_traceChangeManager.CanRedo); + Assert.IsTrue(redoDoneEventFired); } - [TestMethod()] - public void BulkEditTracksLengthTest() + [TestMethod] + public void Reset_HasChanges_ResetsAllChanges() { - //We do an edit on 4 tracks to set the length, undo, redo and again undo. - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var cuesheet = new Cuesheet(); - manager.TraceChanges(cuesheet); - var track1 = new Track(); - var track2 = new Track(); - var track3 = new Track(); - var track4 = new Track(); - manager.TraceChanges(track1); - manager.TraceChanges(track2); - manager.TraceChanges(track3); - manager.TraceChanges(track4); - cuesheet.AddTrack(track1); - cuesheet.AddTrack(track2); - cuesheet.AddTrack(track3); - cuesheet.AddTrack(track4); - track1.IsLinkedToPreviousTrack = true; - track2.IsLinkedToPreviousTrack = true; - track3.IsLinkedToPreviousTrack = true; - track4.IsLinkedToPreviousTrack = true; - var editedTrack = new Track() { Length = new TimeSpan(0, 2, 0) }; - manager.BulkEdit = true; - track1.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - track2.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - track3.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - track4.CopyValues(editedTrack, false, setIsLinkedToPreviousTrack: false, setBegin: false, setEnd: false, setLength: true); - manager.BulkEdit = false; - Assert.IsTrue(manager.CanUndo); - Assert.AreEqual(editedTrack.Length, track1.End); - Assert.AreEqual(track1.End, track2.Begin); - Assert.AreEqual(track2.End, track3.Begin); - Assert.AreEqual(track3.End, track4.Begin); - Assert.IsNotNull(track4.End); - manager.Undo(); - Assert.IsNull(track1.End); - Assert.IsNull(track2.End); - Assert.IsNull(track3.End); - Assert.IsNull(track4.End); - Assert.IsTrue(manager.CanRedo); - manager.Redo(); - Assert.AreEqual(editedTrack.Length, track1.End); - Assert.AreEqual(track1.End, track2.Begin); - Assert.AreEqual(track2.End, track3.Begin); - Assert.AreEqual(track3.End, track4.Begin); - Assert.IsNotNull(track4.End); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - Assert.IsNull(track1.End); - Assert.IsNull(track2.End); - Assert.IsNull(track3.End); - Assert.IsNull(track4.End); + // Arrange + var track = new Track() + { + Artist = "Track 1 Artist", + Title = "Track 1 Title", + End = new TimeSpan(0, 2, 30) + }; + var cuesheet = new Cuesheet() + { + Tracks = [track] + }; + _traceChangeManager.AddChange(new(cuesheet, new(Enumerable.Empty<Track>(), nameof(Cuesheet.Tracks)))); + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.End)))); + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Title)))); + _traceChangeManager.AddChange(new(track, new(null, nameof(Track.Artist)))); + _traceChangeManager.Undo(); + // Act + _traceChangeManager.Reset(); + // Assert + Assert.IsFalse(_traceChangeManager.CanUndo); + Assert.IsFalse(_traceChangeManager.CanRedo); } - + [TestMethod()] public void RemoveTracedChanges_RemovesChanges_WhenChangesAvailable() { // Arrange - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); - var cuesheet1 = new Cuesheet(); - var cuesheet2 = new Cuesheet(); - manager.TraceChanges(cuesheet1); - manager.TraceChanges(cuesheet2); - cuesheet1.Artist = "Test Artist Cuesheet 1"; - cuesheet1.Title = "Test Title Cuesheet 1"; - cuesheet2.CDTextfile = new("CD Testfile.cdt"); - cuesheet2.Title = "Test Title Cuesheet 2"; + var cuesheet1 = new Cuesheet() + { + Artist = "Test Artist Cuesheet 1", + Title = "Test Title Cuesheet 1" + }; + var cuesheet2 = new Cuesheet() + { + CDTextfile = new("CD Testfile.cdt"), + Title = "Test Title Cuesheet 2" + }; + _traceChangeManager.AddChange(new(cuesheet1, new(null, nameof(Cuesheet.Artist)))); + _traceChangeManager.AddChange(new(cuesheet1, new(null, nameof(Cuesheet.Title)))); + _traceChangeManager.AddChange(new(cuesheet2, new(null, nameof(Cuesheet.CDTextfile)))); + _traceChangeManager.AddChange(new(cuesheet2, new(null, nameof(Cuesheet.Title)))); var tracedObjectHistoryChangedFired = false; - manager.TracedObjectHistoryChanged += delegate + _traceChangeManager.TracedObjectHistoryChanged += delegate { tracedObjectHistoryChangedFired = true; }; // Act - manager.RemoveTracedChanges([cuesheet1]); + _traceChangeManager.RemoveTracedChanges([cuesheet1]); // Assert Assert.IsTrue(tracedObjectHistoryChangedFired); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - manager.Undo(); - Assert.IsFalse(manager.CanUndo); + Assert.IsTrue(_traceChangeManager.CanUndo); + _traceChangeManager.Undo(); + _traceChangeManager.Undo(); + Assert.IsFalse(_traceChangeManager.CanUndo); + Assert.IsNull(cuesheet2.CDTextfile); + Assert.IsNull(cuesheet2.Title); } [TestMethod()] public void RemoveTracedChanges_RemovesNoChanges_WhenNoChangesAvailable() { // Arrange - var manager = new TraceChangeManager(TestHelper.CreateLogger<TraceChangeManager>()); var cuesheet1 = new Cuesheet(); - var cuesheet2 = new Cuesheet(); - manager.TraceChanges(cuesheet1); - manager.TraceChanges(cuesheet2); - cuesheet2.CDTextfile = new("CD Testfile.cdt"); - cuesheet2.Title = "Test Title Cuesheet 2"; + var cuesheet2 = new Cuesheet() + { + CDTextfile = new("CD Testfile.cdt"), + Title = "Test Title Cuesheet 2" + }; + _traceChangeManager.AddChange(new(cuesheet2, new(null, nameof(Cuesheet.CDTextfile)))); + _traceChangeManager.AddChange(new(cuesheet2, new(null, nameof(Cuesheet.Title)))); var tracedObjectHistoryChangedFired = false; - manager.TracedObjectHistoryChanged += delegate + _traceChangeManager.TracedObjectHistoryChanged += delegate { tracedObjectHistoryChangedFired = true; }; // Act - manager.RemoveTracedChanges([cuesheet1]); + _traceChangeManager.RemoveTracedChanges([cuesheet1]); // Assert Assert.IsTrue(tracedObjectHistoryChangedFired); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - manager.Undo(); - Assert.IsFalse(manager.CanUndo); + Assert.IsTrue(_traceChangeManager.CanUndo); + _traceChangeManager.Undo(); + _traceChangeManager.Undo(); + Assert.IsFalse(_traceChangeManager.CanUndo); + Assert.IsNull(cuesheet2.CDTextfile); + Assert.IsNull(cuesheet2.Title); } } } \ No newline at end of file diff --git a/AudioCuesheetEditor/AudioCuesheetEditor.csproj b/AudioCuesheetEditor/AudioCuesheetEditor.csproj index 171d7120..3aea3302 100644 --- a/AudioCuesheetEditor/AudioCuesheetEditor.csproj +++ b/AudioCuesheetEditor/AudioCuesheetEditor.csproj @@ -6,8 +6,8 @@ <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <PackageProjectUrl>https://github.com/NeoCoderMatrix86/AudioCuesheetEditor</PackageProjectUrl> - <RazorLangVersion>3.0</RazorLangVersion> - <Version>12.0.0</Version> + <RazorLangVersion>Latest</RazorLangVersion> + <Version>12.1.0</Version> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <PublishTrimmed>true</PublishTrimmed> <RunAOTCompilation>true</RunAOTCompilation> @@ -22,15 +22,15 @@ <ItemGroup> <PackageReference Include="HtmlSanitizer" Version="9.0.892" /> <PackageReference Include="MetaBrainz.MusicBrainz" Version="8.0.1" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.5" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.5" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.7" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.7" PrivateAssets="all" /> <PackageReference Include="BlazorDownloadFile" Version="2.4.0.2" /> <PackageReference Include="Howler.Blazor" Version="0.10.0" /> - <PackageReference Include="Markdig" Version="1.1.1" /> - <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.5" /> - <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="10.0.5" /> - <PackageReference Include="MudBlazor" Version="9.2.0" /> - <PackageReference Include="Toolbelt.Blazor.HotKeys2" Version="6.2.0" /> + <PackageReference Include="Markdig" Version="1.2.0" /> + <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.7" /> + <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="10.0.7" /> + <PackageReference Include="MudBlazor" Version="9.4.0" /> + <PackageReference Include="Toolbelt.Blazor.HotKeys2" Version="6.2.1" /> </ItemGroup> <ItemGroup> diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs index 65ea5d96..501f90cf 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs @@ -15,113 +15,24 @@ //<http: //www.gnu.org/licenses />. using AudioCuesheetEditor.Model.Entity; using AudioCuesheetEditor.Model.IO.Audio; -using AudioCuesheetEditor.Model.UI; using System.Text.Json.Serialization; namespace AudioCuesheetEditor.Model.AudioCuesheet { - public enum MoveDirection + public class Cuesheet() : Validateable, ICuesheet { - Up, - Down - } - - public class TracksAddedRemovedEventArgs(IEnumerable<Track> tracks) : EventArgs - { - public IEnumerable<Track> Tracks { get; } = tracks; - } - - public class Cuesheet() : Validateable, ITraceable, ICuesheet - { - private readonly Lock syncLock = new(); - - private List<Track> tracks = []; - private String? artist; - private String? title; - private Audiofile? audiofile; - private CDTextfile? cDTextfile; - private String? catalogueNumber; - private readonly List<KeyValuePair<String, Track>> currentlyHandlingLinkedTrackPropertyChange = []; - - public event EventHandler<TraceablePropertiesChangedEventArgs>? TraceablePropertyChanged; - public event EventHandler? IsRecordingChanged; - public event EventHandler? AudiofileChanged; - [JsonInclude] - public IReadOnlyCollection<Track> Tracks - { - get => tracks.AsReadOnly(); - private set - { - foreach (var track in tracks) - { - track.RankPropertyValueChanged -= Track_RankPropertyValueChanged; - track.IsLinkedToPreviousTrackChanged -= Track_IsLinkedToPreviousTrackChanged; - track.Cuesheet = null; - } - tracks = [.. value]; - foreach (var track in tracks) - { - track.Cuesheet = this; - track.RankPropertyValueChanged += Track_RankPropertyValueChanged; - track.IsLinkedToPreviousTrackChanged += Track_IsLinkedToPreviousTrackChanged; - } - } - } + public IEnumerable<Track> Tracks { get; set; } = []; - public String? Artist - { - get => artist; - set - { - var previousValue = artist; - artist = value; - FireEvents(previousValue, propertyName: nameof(Artist)); - } - } - public String? Title - { - get => title; - set - { - var previousValue = title; - title = value; - FireEvents(previousValue, propertyName: nameof(Title)); - } - } - public Audiofile? Audiofile - { - get => audiofile; - set - { - var previousValue = audiofile; - audiofile = value; - FireEvents(previousValue, propertyName: nameof(Audiofile)); - AudiofileChanged?.Invoke(this, EventArgs.Empty); - } - } + public String? Artist { get; set; } + + public String? Title { get; set; } - public CDTextfile? CDTextfile - { - get => cDTextfile; - set - { - var previousValue = cDTextfile; - cDTextfile = value; - FireEvents(previousValue, fireValidateablePropertyChanged: false, propertyName: nameof(CDTextfile)); - } - } + public Audiofile? Audiofile { get; set; } - public String? Cataloguenumber - { - get => catalogueNumber; - set - { - var previousValue = catalogueNumber; - catalogueNumber = value; - FireEvents(previousValue, propertyName: nameof(Cataloguenumber)); - } - } + public CDTextfile? CDTextfile { get; set; } + + public String? Cataloguenumber { get; set; } [JsonIgnore] public bool IsRecording => RecordingStart.HasValue; @@ -129,231 +40,6 @@ public String? Cataloguenumber [JsonIgnore] public DateTime? RecordingStart { get; set; } - [JsonIgnore] - public IEnumerable<String> IsRecordingPossible - { - get - { - var errors = new List<String>(); - if (IsRecording) - { - errors.Add("Record is already running!"); - } - if (Tracks.Count != 0) - { - errors.Add("Cuesheet already contains tracks!"); - } - return errors; - } - } - - [JsonIgnore] - public Boolean IsImporting { get; set; } - - /// <summary> - /// Get the previous linked track of a track object - /// </summary> - /// <param name="track">Track object to get the previous link to</param> - /// <returns>Previous linked track or null (if not linked)</returns> - public Track? GetPreviousLinkedTrack(Track track) - { - Track? previousLinkedTrack = null; - if (track.IsLinkedToPreviousTrack) - { - var index = tracks.IndexOf(track); - if (index > 0) - { - previousLinkedTrack = tracks.ElementAt(index - 1); - } - } - return previousLinkedTrack; - } - - public void AddTrack(Track track) - { - if (track.IsCloned) - { - throw new ArgumentException("Cloned tracks may not be added!"); - } - var previousValue = new List<Track>(tracks); - track.IsLinkedToPreviousTrackChanged += Track_IsLinkedToPreviousTrackChanged; - if (IsRecording && RecordingStart.HasValue) - { - track.Begin = DateTime.UtcNow - RecordingStart.Value; - } - //Fire the event manually since we don't know if the track is already linked to previous one - Track_IsLinkedToPreviousTrackChanged(track, EventArgs.Empty); - tracks.Add(track); - track.Cuesheet = this; - RecalculateTrackProperties(track); - track.RankPropertyValueChanged += Track_RankPropertyValueChanged; - OnTraceablePropertyChanged(previousValue, nameof(Tracks)); - } - - public void RemoveTrack(Track track) - { - var index = tracks.IndexOf(track); - Track? nextTrack = null; - if ((index + 1) < tracks.Count) - { - if (tracks.ElementAt(index + 1).IsLinkedToPreviousTrack) - { - nextTrack = tracks.ElementAt(index + 1); - } - } - var previousValue = new List<Track>(); - tracks.ForEach(x => previousValue.Add(new Track(x))); - tracks.Remove(track); - track.Cuesheet = null; - track.RankPropertyValueChanged -= Track_RankPropertyValueChanged; - track.IsLinkedToPreviousTrackChanged -= Track_IsLinkedToPreviousTrackChanged; - //If Tracks are linked, we need to set the linked track again - if (nextTrack != null) - { - index = tracks.IndexOf(nextTrack); - if (index > 0) - { - var previousTrack = tracks.ElementAt(index - 1); - if (previousTrack.Position.HasValue) - { - nextTrack.Position = previousTrack.Position.Value + 1; - } - if (previousTrack.End.HasValue) - { - nextTrack.Begin = previousTrack.End.Value; - } - } - } - RecalculateLastTrackEnd(); - OnTraceablePropertyChanged(previousValue, nameof(Tracks)); - } - - /// <summary> - /// Remove selected tracks - /// </summary> - /// <param name="tracksToRemove">Selected tracks to remove (can not be null, only empty)</param> - public void RemoveTracks(IReadOnlyCollection<Track> tracksToRemove) - { - var previousValue = new List<Track>(); - tracks.ForEach(x => previousValue.Add(new Track(x))); - tracks.ForEach(x => x.RankPropertyValueChanged -= Track_RankPropertyValueChanged); - tracks.ForEach(x => x.IsLinkedToPreviousTrackChanged -= Track_IsLinkedToPreviousTrackChanged); - var intersection = tracks.Intersect(tracksToRemove); - tracks = [.. tracks.Except(intersection)]; - foreach (var track in tracks) - { - if (track.IsLinkedToPreviousTrack) - { - track.Position = (uint)tracks.IndexOf(track) + 1; - } - var previousTrack = GetPreviousLinkedTrack(track); - if (previousTrack != null) - { - if (previousTrack.End.HasValue) - { - track.Begin = previousTrack.End; - } - } - } - tracks.ForEach(x => x.RankPropertyValueChanged += Track_RankPropertyValueChanged); - tracks.ForEach(x => x.IsLinkedToPreviousTrackChanged += Track_IsLinkedToPreviousTrackChanged); - RecalculateLastTrackEnd(); - OnTraceablePropertyChanged(previousValue, nameof(Tracks)); - } - public Boolean MoveTracksPossible(IEnumerable<Track> tracksToMove, MoveDirection moveDirection) - { - lock (syncLock) - { - var trackIndices = tracksToMove.Select(t => tracks.IndexOf(t)).Where(i => i >= 0).OrderBy(i => i).ToList(); - - if (trackIndices.Count == 0) - { - return false; - } - - if (moveDirection == MoveDirection.Up) - { - return trackIndices.First() > 0; - } - if (moveDirection == MoveDirection.Down) - { - return trackIndices.Last() < Tracks.Count - 1; - } - - return false; - } - } - public void MoveTracks(IEnumerable<Track> tracksToMove, MoveDirection moveDirection) - { - lock (syncLock) - { - if (!MoveTracksPossible(tracksToMove, moveDirection)) - { - return; - } - var trackIndices = tracksToMove.Select(t => tracks.IndexOf(t)).Where(i => i >= 0).OrderBy(i => i).ToList(); - - var previousValue = new List<Track>(Tracks); - - if (moveDirection == MoveDirection.Up) - { - foreach (var index in trackIndices) - { - if (index > 0) - { - SwitchTracks(tracks[index], tracks[index - 1]); - } - } - } - else if (moveDirection == MoveDirection.Down) - { - for (int i = trackIndices.Count - 1; i >= 0; i--) - { - int index = trackIndices[i]; - if (index < Tracks.Count - 1) - { - SwitchTracks(tracks[index], tracks[index + 1]); - } - } - } - - OnTraceablePropertyChanged(previousValue, nameof(Tracks)); - } - } - - public void StartRecording() - { - if (IsRecordingPossible.Any() == false) - { - RecordingStart = DateTime.UtcNow; - IsRecordingChanged?.Invoke(this, EventArgs.Empty); - } - } - - public void StopRecording() - { - //Set end of last track - var lastTrack = Tracks.LastOrDefault(); - if ((lastTrack != null) && (RecordingStart.HasValue)) - { - lastTrack.End = DateTime.UtcNow - RecordingStart.Value; - } - RecordingStart = null; - IsRecordingChanged?.Invoke(this, EventArgs.Empty); - } - - public Boolean RecalculateLastTrackEnd() - { - //Try to recalculate length by recalculating last track - var lastTrack = tracks.LastOrDefault(); - if (lastTrack != null) - { - RecalculateTrackProperties(lastTrack); - return true; - } - return false; - } - public override ValidationResult Validate(string property) { ValidationStatus validationStatus = ValidationStatus.NoValidation; @@ -362,7 +48,7 @@ public override ValidationResult Validate(string property) { case nameof(Tracks): validationStatus = ValidationStatus.Success; - if (Tracks.Count <= 0) + if (!Tracks.Any()) { validationMessages ??= []; validationMessages.Add(new ValidationMessage("{0} has invalid Count ({1})!", nameof(Tracks), 0)); @@ -370,7 +56,7 @@ public override ValidationResult Validate(string property) else { //Check track overlapping - var tracksWithSamePosition = Tracks.Where(x => x.IsCloned == false) + var tracksWithSamePosition = Tracks .GroupBy(x => x.Position) .Where(grp => grp.Count() > 1); if (tracksWithSamePosition.Any()) @@ -443,228 +129,5 @@ public override ValidationResult Validate(string property) } return ValidationResult.Create(validationStatus, validationMessages); } - - private void RecalculateTrackProperties(Track trackToCalculate) - { - if ((Audiofile?.Duration.HasValue == true) && (trackToCalculate.End.HasValue == false)) - { - trackToCalculate.End = Audiofile.Duration; - } - if (Tracks.Count > 1) - { - var lastTrack = tracks.ElementAt(tracks.IndexOf(trackToCalculate) - 1); - if (lastTrack != trackToCalculate) - { - if ((Audiofile != null) && (Audiofile.Duration.HasValue) && (lastTrack.End.HasValue) && (lastTrack.End.Value == Audiofile.Duration.Value)) - { - lastTrack.End = null; - } - if (trackToCalculate.Position.HasValue == false) - { - trackToCalculate.Position = lastTrack.Position + 1; - } - if (trackToCalculate.Begin.HasValue == false) - { - trackToCalculate.Begin = lastTrack.End; - } - else - { - if (lastTrack.End.HasValue == false) - { - lastTrack.End = trackToCalculate.Begin; - } - } - if (IsRecording) - { - lastTrack.End = trackToCalculate.Begin; - } - } - } - else - { - if (trackToCalculate.Position.HasValue == false) - { - trackToCalculate.Position = 1; - } - if ((trackToCalculate.Begin.HasValue == false) || (IsRecording)) - { - trackToCalculate.Begin = TimeSpan.Zero; - } - } - } - - private void Track_RankPropertyValueChanged(object? sender, string e) - { - if (sender is Track trackRaisedEvent) - { - var item = KeyValuePair.Create(e, trackRaisedEvent); - if (currentlyHandlingLinkedTrackPropertyChange.Contains(item) == false) - { - currentlyHandlingLinkedTrackPropertyChange.Add(item); - var linkedPreviousTrack = GetPreviousLinkedTrack(trackRaisedEvent); - //Check if raising track has linked previous track - if (trackRaisedEvent.IsLinkedToPreviousTrack && (linkedPreviousTrack != null)) - { - switch (e) - { - case nameof(Track.Position): - if (trackRaisedEvent.Position.HasValue) - { - linkedPreviousTrack.Position = trackRaisedEvent.Position.Value - 1; - } - break; - case nameof(Track.Begin): - if (trackRaisedEvent.Begin.HasValue) - { - linkedPreviousTrack.End = trackRaisedEvent.Begin; - } - break; - } - } - //Check if track is linked by next track - var index = tracks.IndexOf(trackRaisedEvent); - if ((index + 1) < tracks.Count) - { - var nextTrack = tracks.ElementAt(index + 1); - if (nextTrack.IsLinkedToPreviousTrack) - { - switch (e) - { - case nameof(Track.Position): - if (trackRaisedEvent.Position.HasValue) - { - nextTrack.Position = trackRaisedEvent.Position.Value + 1; - } - break; - case nameof(Track.End): - nextTrack.Begin = trackRaisedEvent.End; - break; - } - } - } - currentlyHandlingLinkedTrackPropertyChange.Remove(item); - } - } - else - { - throw new ArgumentNullException(nameof(sender)); - } - } - - private void Track_IsLinkedToPreviousTrackChanged(object? sender, EventArgs e) - { - if (sender != null) - { - Track trackRaisedEvent = (Track)sender; - if (trackRaisedEvent.IsLinkedToPreviousTrack) - { - //Set values - var index = tracks.IndexOf(trackRaisedEvent); - if (index > 0) - { - var previousTrack = tracks.ElementAt(index - 1); - if ((trackRaisedEvent.Position.HasValue) && (previousTrack.Position.HasValue) && (trackRaisedEvent.Position != previousTrack.Position.Value + 1)) - { - trackRaisedEvent.Position = previousTrack.Position.Value + 1; - } - if ((previousTrack.End.HasValue) && (trackRaisedEvent.Begin != previousTrack.End)) - { - trackRaisedEvent.Begin = previousTrack.End; - } - if ((previousTrack.End.HasValue == false) && (trackRaisedEvent.Begin.HasValue)) - { - previousTrack.End = trackRaisedEvent.Begin; - } - } - } - } - else - { - throw new ArgumentNullException(nameof(sender)); - } - } - - private void OnTraceablePropertyChanged(object? previousValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") - { - TraceablePropertyChanged?.Invoke(this, new TraceablePropertiesChangedEventArgs(new TraceableChange(previousValue, propertyName))); - } - - private void SwitchTracks(Track track1, Track track2) - { - var indexTrack1 = tracks.IndexOf(track1); - var indexTrack2 = tracks.IndexOf(track2); - //Switch track positions in array - tracks[indexTrack2] = track1; - tracks[indexTrack1] = track2; - //Set linked tracks correct again - indexTrack1 = tracks.IndexOf(track1); - indexTrack2 = tracks.IndexOf(track2); - //If using linked tracks, we set values correctly - if (track1.IsLinkedToPreviousTrack || track2.IsLinkedToPreviousTrack) - { - //Set values corresponding to their new positions - track1.Position = (uint)indexTrack1 + 1; - track2.Position = (uint)indexTrack2 + 1; - //Set also begin and end - if (indexTrack1 < indexTrack2) - { - track1.Begin = track2.Begin; - (track2.End, track1.End) = (track1.End, track2.End); - if (track2.IsLinkedToPreviousTrack) - { - var previousTrack = GetPreviousLinkedTrack(track2); - if ((previousTrack != null) && (previousTrack.End.HasValue)) - { - track2.Begin = previousTrack.End; - } - } - } - else - { - track2.Begin = track1.Begin; - (track1.End, track2.End) = (track2.End, track1.End); - if (track1.IsLinkedToPreviousTrack) - { - var previousTrack = GetPreviousLinkedTrack(track1); - if ((previousTrack != null) && (previousTrack.End.HasValue)) - { - track1.Begin = previousTrack.End; - } - } - } - } - } - - /// <summary> - /// Method for checking if fire of events should be done - /// </summary> - /// <param name="previousValue">Previous value of the property firing events</param> - /// <param name="fireValidateablePropertyChanged">Fire OnValidateablePropertyChanged?</param> - /// <param name="fireTraceablePropertyChanged">Fire TraceablePropertyChanged?</param> - /// <param name="propertyName">Property firing the event</param> - /// <exception cref="NullReferenceException">If propertyName can not be found, an exception is thrown.</exception> - private void FireEvents(object? previousValue, Boolean fireValidateablePropertyChanged = true, Boolean fireTraceablePropertyChanged = true, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") - { - var propertyInfo = GetType().GetProperty(propertyName); - if (propertyInfo != null) - { - var propertyValue = propertyInfo.GetValue(this); - if (Equals(propertyValue, previousValue) == false) - { - if (fireValidateablePropertyChanged) - { - OnValidateablePropertyChanged(propertyName); - } - if (fireTraceablePropertyChanged) - { - OnTraceablePropertyChanged(previousValue, propertyName); - } - } - } - else - { - throw new NullReferenceException(String.Format("Property {0} could not be found!", propertyName)); - } - } } } diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/ITrack.cs b/AudioCuesheetEditor/Model/AudioCuesheet/ITrack.cs index 38a27e34..36c36a28 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/ITrack.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/ITrack.cs @@ -19,7 +19,7 @@ public interface ITrack { string? Artist { get; set; } string? Title { get; set; } - uint? Position { get; set; } + ushort? Position { get; set; } TimeSpan? Begin { get; set; } TimeSpan? End { get; set; } TimeSpan? Length { get; set; } diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportTrack.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportTrack.cs index b73b1025..213453d0 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportTrack.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportTrack.cs @@ -20,7 +20,7 @@ public class ImportTrack : ITrack { public string? Artist { get; set; } public string? Title { get; set; } - public uint? Position { get; set; } + public ushort? Position { get; set; } public TimeSpan? Begin { get; set; } public TimeSpan? End { get; set; } public TimeSpan? Length { get; set; } diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs index 09b511b7..1efc32ef 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs @@ -14,353 +14,69 @@ //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. using AudioCuesheetEditor.Model.Entity; -using AudioCuesheetEditor.Model.UI; using System.Text.Json.Serialization; namespace AudioCuesheetEditor.Model.AudioCuesheet { - public enum SetFlagMode + public class Track : Validateable, ITrack { - Add, - Remove - } - - public class Track : Validateable, ITraceable, ITrack - { - public static readonly List<String> AllPropertyNames = [nameof(IsLinkedToPreviousTrack), nameof(Position), nameof(Artist), nameof(Title), nameof(Begin), nameof(End), nameof(Flags), nameof(PreGap), nameof(PostGap), nameof(Length)]; - - private uint? position; - private String? artist; - private String? title; - private TimeSpan? begin; - private TimeSpan? end; - private TimeSpan? length; - private List<Flag> flags = []; - private Boolean isLinkedToPreviousTrack; - private Cuesheet? cuesheet; - private TimeSpan? preGap; - private TimeSpan? postGap; - - /// <summary> - /// A property with influence to position of this track in cuesheet has been changed. Name of the property changed is provided in event arguments. - /// </summary> - public event EventHandler<String>? RankPropertyValueChanged; - - /// <summary> - /// Eventhandler for IsLinkedToPreviousTrack has changed - /// </summary> - public event EventHandler? IsLinkedToPreviousTrackChanged; - - /// <inheritdoc/> - public event EventHandler<TraceablePropertiesChangedEventArgs>? TraceablePropertyChanged; - - public Track() { } - - /// <summary> - /// Create object with copied values from input - /// </summary> - /// <param name="track">Object to copy values from</param> - /// <param name="copyCuesheetReference">Copy cuesheet reference from track also?</param> - public Track(ITrack track, Boolean copyCuesheetReference = true) : this() - { - CopyValues(track, copyCuesheetReference); - } - - public uint? Position - { - get => position; - set { var previousValue = position; position = value; FireEvents(previousValue, propertyName: nameof(Position)); } - } - public String? Artist - { - get => artist; - set { var previousValue = artist; artist = value; FireEvents(previousValue, fireRankPropertyValueChanged: false, propertyName: nameof(Artist)); } - } - public String? Title - { - get => title; - set { var previousValue = title; title = value; FireEvents(previousValue, fireRankPropertyValueChanged: false, propertyName: nameof(Title)); } - } - public TimeSpan? Begin - { - get => begin; - set { var previousValue = begin; begin = value; FireEvents(previousValue, propertyName: nameof(Begin)); } - } - public TimeSpan? End - { - get => end; - set { var previousValue = end; end = value; FireEvents(previousValue, propertyName: nameof(End)); } - } - /// <summary> - /// If <see cref="Length"/> is set, should it be automatically change begin and end? Defaulting to true, because only during edit dialog this should be set to false. - /// If set to false an internal field will be used. - /// </summary> - [JsonIgnore] - public Boolean AutomaticallyCalculateLength { get; init; } = true; + public ushort? Position { get; set; } + public String? Artist { get; set; } + public String? Title { get; set; } + public TimeSpan? Begin { get; set; } + public TimeSpan? End { get; set; } [JsonIgnore] public TimeSpan? Length { get { - if (AutomaticallyCalculateLength) + if ((Begin.HasValue == true) && (End.HasValue == true) && (Begin.Value <= End.Value)) { - if ((Begin.HasValue == true) && (End.HasValue == true) && (Begin.Value <= End.Value)) - { - return End - Begin; - } - else - { - return null; - } + return End - Begin; } else { - return length; + return null; } } set { - var previousValue = Length; - if (AutomaticallyCalculateLength) + if ((Begin.HasValue == false) && (End.HasValue == false)) + { + End = value; + } + else { - if ((Begin.HasValue == false) && (End.HasValue == false)) + if ((Begin.HasValue == true) && (End.HasValue == true)) { - Begin = TimeSpan.Zero; End = Begin.Value + value; } else { - if ((Begin.HasValue == true) && (End.HasValue == true)) + if ((End.HasValue == false) && (Begin.HasValue)) { End = Begin.Value + value; } - else + if ((Begin.HasValue == false) && (End.HasValue)) { - if ((End.HasValue == false) && (Begin.HasValue)) - { - End = Begin.Value + value; - } - if ((Begin.HasValue == false) && (End.HasValue)) - { - Begin = End.Value - value; - } + Begin = End.Value - value; } } } - else - { - length = value; - } - FireEvents(previousValue, fireRankPropertyValueChanged: false, fireTraceablePropertyChanged: false); } } [JsonInclude] - public IEnumerable<Flag> Flags - { - get { return flags.AsReadOnly(); } - set - { - var previousValue = flags; - flags = [.. value]; - FireEvents(previousValue, fireValidateablePropertyChanged: false, fireRankPropertyValueChanged: false, propertyName: nameof(Flags)); - } - } - [JsonIgnore] - public Cuesheet? Cuesheet - { - get => cuesheet; - set - { - var previousValue = cuesheet; - Boolean setValue = true; - if (value != null) - { - setValue = value.Tracks.Contains(this); - } - if (setValue) - { - cuesheet = value; - FireEvents(previousValue, fireValidateablePropertyChanged: false, fireRankPropertyValueChanged: false, propertyName: nameof(Cuesheet)); - } - } - } - - /// <summary> - /// Indicates that this track has been cloned from another track and is a transparent proxy - /// </summary> + public IEnumerable<Flag> Flags { get; set; } = []; [JsonIgnore] - public Boolean IsCloned { get { return ClonedFrom != null; } } - /// <summary> - /// Get the original track that this track has been cloned from. Can be null on original objects - /// </summary> - public Track? ClonedFrom { get; set; } + public Cuesheet? Cuesheet { get; set; } /// <inheritdoc/> - public TimeSpan? PreGap - { - get => preGap; - set { var previousValue = preGap; preGap = value; FireEvents(previousValue, fireValidateablePropertyChanged: false, fireRankPropertyValueChanged: false, propertyName: nameof(PreGap)); } - } + public TimeSpan? PreGap { get; set; } /// <inheritdoc/> - public TimeSpan? PostGap - { - get => postGap; - set { var previousValue = postGap; postGap = value; FireEvents(previousValue, fireValidateablePropertyChanged: false, fireRankPropertyValueChanged: false, propertyName: nameof(PostGap)); } - } + public TimeSpan? PostGap { get; set; } /// <summary> /// Set that this track is linked to the previous track in cuesheet /// </summary> - public Boolean IsLinkedToPreviousTrack - { - get => isLinkedToPreviousTrack; - set { var previousValue = IsLinkedToPreviousTrack; isLinkedToPreviousTrack = value; IsLinkedToPreviousTrackChanged?.Invoke(this, EventArgs.Empty); FireEvents(previousValue, fireValidateablePropertyChanged: false, fireRankPropertyValueChanged: false, propertyName: nameof(IsLinkedToPreviousTrack)); } - } - - /// <summary> - /// Performs a deep clone of the current object - /// </summary> - /// <returns>A deep clone of the current object</returns> - public Track Clone() - { - return new Track(this) - { - ClonedFrom = this - }; - } - - /// <summary> - /// Copy values from input object to this object - /// </summary> - /// <param name="track">Object to copy values from</param> - /// <param name="setCuesheet">Copy Cuesheet reference from track also?</param> - /// <param name="setIsLinkedToPreviousTrack">Copy IsLinkedToPreviousTrack from track also?</param> - /// <param name="setPosition">Copy Position from track also?</param> - /// <param name="setArtist">Copy Artist from track also?</param> - /// <param name="setTitle">Copy Title from track also?</param> - /// <param name="setBegin">Copy Begin from track also?</param> - /// <param name="setEnd">Copy End from track also?</param> - /// <param name="setLength">Copy Length from track also?</param> - /// <param name="setFlags">Copy Flags from track also?</param> - /// <param name="setPreGap">Copy PreGap from track also?</param> - /// <param name="setPostGap">Copy PostGap from track also?</param> - /// <param name="useInternalSetters">A list of properties, for whom the internal set construct should be used, in order to avoid automatic calculation.</param> - public void CopyValues(ITrack track, Boolean setCuesheet = true, Boolean setIsLinkedToPreviousTrack = true, Boolean setPosition = true, Boolean setArtist = true, Boolean setTitle = true, Boolean setBegin = true, Boolean setEnd = true, Boolean setLength = false, Boolean setFlags = true, Boolean setPreGap = true, Boolean setPostGap = true, IEnumerable<String>? useInternalSetters = null) - { - if (setCuesheet && (track is Track cuesheetTrack)) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(Cuesheet)))) - { - cuesheet = cuesheetTrack.Cuesheet; - } - else - { - Cuesheet = cuesheetTrack.Cuesheet; - } - } - if (setPosition) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(Position)))) - { - position = track.Position; - } - else - { - Position = track.Position; - } - } - if (setArtist) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(Artist)))) - { - artist = track.Artist; - } - else - { - Artist = track.Artist; - } - } - if (setTitle) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(Title)))) - { - title = track.Title; - } - else - { - Title = track.Title; - } - } - if (setBegin) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(Begin)))) - { - begin = track.Begin; - } - else - { - Begin = track.Begin; - } - } - if (setEnd) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(End)))) - { - end = track.End; - } - else - { - End = track.End; - } - } - if (setLength) - { - Length = track.Length; - } - if (setFlags) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(Flags)))) - { - flags.Clear(); - flags.AddRange(track.Flags); - } - else - { - Flags = track.Flags; - } - } - if (setPreGap) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(PreGap)))) - { - preGap = track.PreGap; - } - else - { - PreGap = track.PreGap; - } - } - if (setPostGap) - { - if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(PostGap)))) - { - postGap = track.PostGap; - } - else - { - PostGap = track.PostGap; - } - } - if (setIsLinkedToPreviousTrack) - { - if (useInternalSetters?.Contains(nameof(IsLinkedToPreviousTrack)) == true) - { - isLinkedToPreviousTrack = track.IsLinkedToPreviousTrack; - } - else - { - IsLinkedToPreviousTrack = track.IsLinkedToPreviousTrack; - } - } - } + public Boolean IsLinkedToPreviousTrack { get; set; } public override ValidationResult Validate(string property) { @@ -385,7 +101,7 @@ public override ValidationResult Validate(string property) else { // Check correct track position - if ((IsCloned == false) && (Cuesheet != null)) + if (Cuesheet != null) { var positionTrackShouldHave = Cuesheet.Tracks.OrderBy(x => x.Begin ?? TimeSpan.MaxValue).ThenBy(x => x.Position).ToList().IndexOf(this) + 1; if (positionTrackShouldHave != Position) @@ -450,47 +166,5 @@ public override ValidationResult Validate(string property) } return ValidationResult.Create(validationStatus, validationMessages); } - - protected void OnTraceablePropertyChanged(object? previousValue, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") - { - TraceablePropertyChanged?.Invoke(this, new TraceablePropertiesChangedEventArgs(new TraceableChange(previousValue, propertyName))); - } - - /// <summary> - /// Method for checking if fire of events should be done - /// </summary> - /// <param name="previousValue">Previous value of the property firing events</param> - /// <param name="fireValidateablePropertyChanged">Fire OnValidateablePropertyChanged?</param> - /// <param name="fireRankPropertyValueChanged">Fire RankPropertyValueChanged?</param> - /// <param name="fireTraceablePropertyChanged">Fire OnTraceablePropertyChanged?</param> - /// <exception cref="NullReferenceException">If propertyName can not be found, an exception is thrown.</exception> - /// <param name="propertyName">Property firing the events</param> - private void FireEvents(object? previousValue, Boolean fireValidateablePropertyChanged = true, Boolean fireRankPropertyValueChanged = true, Boolean fireTraceablePropertyChanged = true, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") - { - var propertyInfo = GetType().GetProperty(propertyName); - if (propertyInfo != null) - { - var propertyValue = propertyInfo.GetValue(this); - if (Equals(propertyValue, previousValue) == false) - { - if (fireValidateablePropertyChanged) - { - OnValidateablePropertyChanged(propertyName); - } - if (fireRankPropertyValueChanged) - { - RankPropertyValueChanged?.Invoke(this, propertyName); - } - if (fireTraceablePropertyChanged) - { - OnTraceablePropertyChanged(previousValue, propertyName); - } - } - } - else - { - throw new NullReferenceException(String.Format("Property {0} could not be found!", propertyName)); - } - } } } diff --git a/AudioCuesheetEditor/Model/Entity/IValidateable.cs b/AudioCuesheetEditor/Model/Entity/IValidateable.cs index 4d88ec34..8a848945 100644 --- a/AudioCuesheetEditor/Model/Entity/IValidateable.cs +++ b/AudioCuesheetEditor/Model/Entity/IValidateable.cs @@ -28,8 +28,6 @@ public interface IValidateable /// <param name="property"></param> /// <returns></returns> ValidationResult Validate(String property); - - event EventHandler<String>? ValidateablePropertyChanged; } public enum ValidationStatus { diff --git a/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs b/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs index ebee7f8d..b6d86c42 100644 --- a/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs +++ b/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs @@ -101,19 +101,5 @@ public String? AudioFileType return audioFileType; } } - - [JsonIgnore] - public Boolean PlaybackPossible - { - get - { - Boolean playbackPossible = false; - if ((String.IsNullOrEmpty(Name) == false) && (String.IsNullOrEmpty(ObjectURL) == false) && (String.IsNullOrEmpty(AudioFileType) == false) && (AudioCodec != null)) - { - playbackPossible = true; - } - return playbackPossible; - } - } } } diff --git a/AudioCuesheetEditor/Model/IO/Projectfile.cs b/AudioCuesheetEditor/Model/IO/Projectfile.cs index c6d2617e..a1e187bc 100644 --- a/AudioCuesheetEditor/Model/IO/Projectfile.cs +++ b/AudioCuesheetEditor/Model/IO/Projectfile.cs @@ -36,7 +36,15 @@ public class Projectfile(Cuesheet cuesheet) public static Cuesheet? ImportFile(string fileContent) { - return JsonSerializer.Deserialize<Cuesheet>(fileContent, Options); + var cuesheet = JsonSerializer.Deserialize<Cuesheet>(fileContent, Options); + if (cuesheet != null) + { + foreach (var track in cuesheet.Tracks) + { + track.Cuesheet = cuesheet; + } + } + return cuesheet; } public Cuesheet Cuesheet { get; private set; } = cuesheet; diff --git a/AudioCuesheetEditor/Model/UI/EditMultipleTracksModalResult.cs b/AudioCuesheetEditor/Model/UI/EditMultipleTracksModalResult.cs index 5fcefbd1..3aefd35e 100644 --- a/AudioCuesheetEditor/Model/UI/EditMultipleTracksModalResult.cs +++ b/AudioCuesheetEditor/Model/UI/EditMultipleTracksModalResult.cs @@ -24,11 +24,10 @@ public enum DynamicEditValue EnteredValueAdd = 2, EnteredValueSubstract = 3 } - public class EditMultipleTracksModalResult(Track editedTrack, DynamicEditValue isLinkedToPreviousTrackChanged, DynamicEditValue positionEditMode, DynamicEditValue artistEditMode, DynamicEditValue titleEditMode, DynamicEditValue beginEditMode, DynamicEditValue endEditMode, DynamicEditValue lengthEditMode, DynamicEditValue flagsEditMode, DynamicEditValue pregapEditMode, DynamicEditValue postgapEditMode) + public class EditMultipleTracksModalResult(Track editedTrack, DynamicEditValue isLinkedToPreviousTrackChanged, DynamicEditValue artistEditMode, DynamicEditValue titleEditMode, DynamicEditValue beginEditMode, DynamicEditValue endEditMode, DynamicEditValue lengthEditMode, DynamicEditValue flagsEditMode, DynamicEditValue pregapEditMode, DynamicEditValue postgapEditMode) { public Track EditedTrack { get; } = editedTrack; public DynamicEditValue IsLinkedToPreviousTrackEditMode { get; } = isLinkedToPreviousTrackChanged; - public DynamicEditValue PositionEditMode { get; } = positionEditMode; public DynamicEditValue ArtistEditMode { get; } = artistEditMode; public DynamicEditValue TitleEditMode { get; } = titleEditMode; public DynamicEditValue BeginEditMode { get; } = beginEditMode; diff --git a/AudioCuesheetEditor/Model/UI/ITraceable.cs b/AudioCuesheetEditor/Model/UI/TraceableChange.cs similarity index 67% rename from AudioCuesheetEditor/Model/UI/ITraceable.cs rename to AudioCuesheetEditor/Model/UI/TraceableChange.cs index 7db690eb..e5dfa0ba 100644 --- a/AudioCuesheetEditor/Model/UI/ITraceable.cs +++ b/AudioCuesheetEditor/Model/UI/TraceableChange.cs @@ -30,20 +30,4 @@ public TraceableChange(object? previousValue, string propertyName) public object? PreviousValue { get; } public string PropertyName { get; } } - - public class TraceablePropertiesChangedEventArgs(TraceableChange traceableChange) : EventArgs - { - public TraceableChange TraceableChange { get; } = traceableChange; - } - - /// <summary> - /// Interface for undo/redoable actions - /// </summary> - public interface ITraceable - { - /// <summary> - /// A property of this object has been changed and therefore needs to be traced by the TraceChangeManager. - /// </summary> - public event EventHandler<TraceablePropertiesChangedEventArgs> TraceablePropertyChanged; - } } diff --git a/AudioCuesheetEditor/Model/UI/TracedChange.cs b/AudioCuesheetEditor/Model/UI/TracedChange.cs new file mode 100644 index 00000000..fb4eb9a9 --- /dev/null +++ b/AudioCuesheetEditor/Model/UI/TracedChange.cs @@ -0,0 +1,39 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//<http: //www.gnu.org/licenses />. +namespace AudioCuesheetEditor.Model.UI +{ + /// <summary> + /// Class for tracing changes on an object + /// </summary> + public class TracedChange(object traceableObject, TraceableChange traceableChange) + { + private readonly WeakReference<object> _tracedObject = new(traceableObject, false); + + public object? TraceableObject + { + get + { + if (_tracedObject.TryGetTarget(out var traceable)) + { + return traceable; + } + return null; + } + } + + public TraceableChange TraceableChange { get; } = traceableChange; + } +} diff --git a/AudioCuesheetEditor/Model/UI/TracedChanges.cs b/AudioCuesheetEditor/Model/UI/TracedChanges.cs new file mode 100644 index 00000000..00ce7c1d --- /dev/null +++ b/AudioCuesheetEditor/Model/UI/TracedChanges.cs @@ -0,0 +1,23 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//<http: //www.gnu.org/licenses />. +namespace AudioCuesheetEditor.Model.UI +{ + public class TracedChanges(IEnumerable<TracedChange> changes) + { + public List<TracedChange> Changes { get; } = [.. changes]; + public bool HasTraceableObject { get { return Changes.Any(x => x.TraceableObject != null); } } + } +} diff --git a/AudioCuesheetEditor/Pages/Index.razor b/AudioCuesheetEditor/Pages/Index.razor index 8fe46021..445ba103 100644 --- a/AudioCuesheetEditor/Pages/Index.razor +++ b/AudioCuesheetEditor/Pages/Index.razor @@ -25,6 +25,7 @@ along with Foobar. If not, see @inject ISessionStateContainer _sessionStateContainer @inject DialogManager _dialogManager @inject ISnackbar _snackbar +@inject ICuesheetManager _cuesheetManager <CascadingValue Value="currentViewmode"> <MudTabs Rounded ApplyEffectsToContainer Outlined Color="Color.Success" ActivePanelIndex="(int)currentViewmode" ActivePanelIndexChanged="this.AsNonRenderingEventHandler<int>(ActiveTabIndexChanged)"> @@ -44,18 +45,6 @@ along with Foobar. If not, see ViewOptions? options; ViewMode currentViewmode = ViewMode.DetailView; - public Cuesheet? Cuesheet - { - get - { - if (currentViewmode == ViewMode.ImportView) - { - return _sessionStateContainer.ImportCuesheet; - } - return _sessionStateContainer.Cuesheet; - } - } - protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); @@ -112,17 +101,21 @@ along with Foobar. If not, see switch (_sessionStateContainer.Importfile?.FileType) { case ImportFileType.ProjectFile: + await _importManager.AnalyseImportfile(); + _sessionStateContainer.ResetImport(); + break; case ImportFileType.Cuesheet: await _importManager.AnalyseImportfile(); + _importManager.ImportCuesheet(); _sessionStateContainer.ResetImport(); break; case ImportFileType.Textfile: await LocalStorageOptionsProvider.SaveOptionsValueAsync<ViewOptions>(x => x.ActiveTab, ViewMode.ImportView); break; } - if ((Cuesheet != null) && (_sessionStateContainer.ImportAudiofile != null)) + if (_sessionStateContainer.ImportAudiofile != null) { - Cuesheet.Audiofile = _sessionStateContainer.ImportAudiofile; + _cuesheetManager.SetProperty(x => x.Audiofile, _sessionStateContainer.ImportAudiofile); } } finally diff --git a/AudioCuesheetEditor/Program.cs b/AudioCuesheetEditor/Program.cs index ce09002c..031766d3 100644 --- a/AudioCuesheetEditor/Program.cs +++ b/AudioCuesheetEditor/Program.cs @@ -19,6 +19,7 @@ using AudioCuesheetEditor.Extensions; using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Services.Audio; +using AudioCuesheetEditor.Services.AudioCuesheet; using AudioCuesheetEditor.Services.IO; using AudioCuesheetEditor.Services.UI; using AudioCuesheetEditor.Services.Validation; @@ -48,7 +49,13 @@ builder.Services.AddScoped<ILocalStorageOptionsProvider, LocalStorageOptionsProvider>(); builder.Services.AddScoped<MusicBrainzDataProvider>(); -builder.Services.AddScoped<ISessionStateContainer, SessionStateContainer>(); +builder.Services.AddScoped<ISessionStateContainer>(x => +{ + var localStorageOptionsProvider = x.GetRequiredService<ILocalStorageOptionsProvider>(); + var sessionStateContainer = new SessionStateContainer(localStorageOptionsProvider); + _ = sessionStateContainer.InitializeAsync(); + return sessionStateContainer; +}); builder.Services.AddScoped<ITraceChangeManager, TraceChangeManager>(); builder.Services.AddScoped<ImportManager>(); builder.Services.AddScoped<ITextImportService, TextImportService>(); @@ -62,6 +69,8 @@ builder.Services.AddScoped<DialogManager>(); builder.Services.AddScoped<ExportfileGenerator>(); builder.Services.AddScoped<AutocompleteManager>(); +builder.Services.AddScoped<ITrackManager, TrackManager>(); +builder.Services.AddScoped<ICuesheetManager, CuesheetManager>(); builder.Services.AddLogging(); // Read out configuration for loglevel diff --git a/AudioCuesheetEditor/Services/Audio/PlaybackService.cs b/AudioCuesheetEditor/Services/Audio/PlaybackService.cs index b451800e..68142028 100644 --- a/AudioCuesheetEditor/Services/Audio/PlaybackService.cs +++ b/AudioCuesheetEditor/Services/Audio/PlaybackService.cs @@ -25,42 +25,44 @@ public class PlaybackService : IDisposable private readonly ISessionStateContainer _sessionStateContainer; private readonly IHowl _howl; - private int? soundId; - private Audiofile? currentlyPlayingAudiofile; - private Timer? updateTimer; - private bool disposedValue; - private readonly Lock timerLock = new(); - private TimeSpan? currentPosition; - private Cuesheet cuesheet; + private int? _currentPlayingSoundId; + private Audiofile? _currentlyPlayingAudiofile; + private Timer? _updateTimer; + private bool _disposedValue; + private readonly Lock _timerLock = new(); + private TimeSpan? _currentPosition; public event Action? CurrentPositionChanged; public TimeSpan? CurrentPosition { - get => currentPosition; + get => _currentPosition; private set { - if (currentPosition != value) + if (_currentPosition != value) { - currentPosition = value; + _currentPosition = value; CurrentPositionChanged?.Invoke(); } } } - public Track? CurrentlyPlayingTrack => cuesheet.Tracks.SingleOrDefault(x => x.Begin.HasValue == true && x.End.HasValue == true && x.Begin <= CurrentPosition && x.End > CurrentPosition); - public TimeSpan? TotalTime => cuesheet.Audiofile?.Duration; + public Track? CurrentlyPlayingTrack => _sessionStateContainer.Cuesheet.Tracks.SingleOrDefault(x => x.Begin.HasValue == true && x.End.HasValue == true && x.Begin <= CurrentPosition && x.End > CurrentPosition); + public TimeSpan? TotalTime => _sessionStateContainer.Cuesheet.Audiofile?.Duration; public Boolean IsPlaying { get; private set; } = false; - //Refer to Cuesheet (not ImportCuesheet) since playback will always be done on the imported cuesheet - public Boolean PlaybackPossible => cuesheet.Audiofile != null && cuesheet.Audiofile.PlaybackPossible; - public Boolean PreviousPossible => (CurrentlyPlayingTrack != null) && cuesheet.Tracks.ToList().IndexOf(CurrentlyPlayingTrack) >= 1; - public Boolean NextPossible => (CurrentlyPlayingTrack != null) && cuesheet.Tracks.ToList().IndexOf(CurrentlyPlayingTrack) < cuesheet.Tracks.Count - 1; + public Boolean IsPlaybackPossible + { + get + { + var audiofile = _sessionStateContainer.Cuesheet.Audiofile; + return String.IsNullOrEmpty(audiofile?.ObjectURL) == false && String.IsNullOrEmpty(audiofile?.AudioFileType) == false; + } + } + public Boolean IsPreviousPossible => (CurrentlyPlayingTrack != null) && _sessionStateContainer.Cuesheet.Tracks.FirstOrDefault(x => x.End <= CurrentlyPlayingTrack.Begin) != null; + public Boolean IsNextPossible => (CurrentlyPlayingTrack != null) && _sessionStateContainer.Cuesheet.Tracks.FirstOrDefault(x => x.Begin >= CurrentlyPlayingTrack.End) != null; public PlaybackService(ISessionStateContainer sessionStateContainer, IHowl howl) { _sessionStateContainer = sessionStateContainer; - cuesheet = _sessionStateContainer.Cuesheet; - cuesheet.AudiofileChanged += Cuesheet_AudiofileChanged; - _sessionStateContainer.CuesheetChanged += SessionStateContainer_CuesheetChanged; _howl = howl; _howl.OnPlay += Howl_OnPlay; _howl.OnPause += Howl_OnPause; @@ -71,28 +73,28 @@ public PlaybackService(ISessionStateContainer sessionStateContainer, IHowl howl) public async Task PlayOrPauseAsync() { //Reset if the last played audiofile is not the current one - if (currentlyPlayingAudiofile != cuesheet.Audiofile) + if (_currentlyPlayingAudiofile != _sessionStateContainer.Cuesheet.Audiofile) { - soundId = null; + _currentPlayingSoundId = null; } //If the current audiofile already started, we just pause - if (soundId != null) + if (_currentPlayingSoundId != null) { - await _howl.Pause(soundId.Value); + await _howl.Pause(_currentPlayingSoundId.Value); } else { - if (PlaybackPossible) + if (IsPlaybackPossible) { string[]? sources = null; string[]? formats = null; - if (cuesheet.Audiofile?.ObjectURL != null) + if (_sessionStateContainer.Cuesheet.Audiofile?.ObjectURL != null) { - sources = [cuesheet.Audiofile.ObjectURL]; + sources = [_sessionStateContainer.Cuesheet.Audiofile.ObjectURL]; } - if (cuesheet.Audiofile?.AudioFileType != null) + if (_sessionStateContainer.Cuesheet.Audiofile?.AudioFileType != null) { - formats = [cuesheet.Audiofile.AudioFileType.ToLower()]; + formats = [_sessionStateContainer.Cuesheet.Audiofile.AudioFileType.ToLower()]; } var options = new HowlOptions { @@ -100,8 +102,8 @@ public async Task PlayOrPauseAsync() Formats = formats, Html5 = true }; - soundId = await _howl.Play(options); - currentlyPlayingAudiofile = cuesheet.Audiofile; + _currentPlayingSoundId = await _howl.Play(options); + _currentlyPlayingAudiofile = _sessionStateContainer.Cuesheet.Audiofile; } } } @@ -114,18 +116,18 @@ public async Task PlayAsync(Track trackToPlay) { await PlayOrPauseAsync(); } - if (soundId.HasValue) + if (_currentPlayingSoundId.HasValue) { - await _howl.Seek(soundId.Value, trackToPlay.Begin.Value); + await _howl.Seek(_currentPlayingSoundId.Value, trackToPlay.Begin.Value); } } } public async Task StopAsync() { - if (soundId != null) + if (_currentPlayingSoundId != null) { - await _howl.Stop(soundId.Value); + await _howl.Stop(_currentPlayingSoundId.Value); } } @@ -133,8 +135,7 @@ public async Task PlayNextTrackAsync() { if (CurrentlyPlayingTrack != null) { - var index = cuesheet.Tracks.ToList().IndexOf(CurrentlyPlayingTrack); - var trackToPlay = cuesheet.Tracks.ElementAtOrDefault(index + 1); + var trackToPlay = _sessionStateContainer.Cuesheet.Tracks.FirstOrDefault(x => x.Begin >= CurrentlyPlayingTrack.End); if (trackToPlay != null) { await PlayAsync(trackToPlay); @@ -146,8 +147,7 @@ public async Task PlayPreviousTrackAsync() { if (CurrentlyPlayingTrack != null) { - var index = cuesheet.Tracks.ToList().IndexOf(CurrentlyPlayingTrack); - var trackToPlay = cuesheet.Tracks.ElementAtOrDefault(index - 1); + var trackToPlay = _sessionStateContainer.Cuesheet.Tracks.LastOrDefault(x => x.End <= CurrentlyPlayingTrack.Begin); if (trackToPlay != null) { await PlayAsync(trackToPlay); @@ -157,17 +157,17 @@ public async Task PlayPreviousTrackAsync() public async Task SeekAsync(TimeSpan time) { - if (soundId.HasValue == false) + if (_currentPlayingSoundId.HasValue == false) { await PlayOrPauseAsync(); } - if (soundId.HasValue) + if (_currentPlayingSoundId.HasValue) { if (IsPlaying == false) { await PlayOrPauseAsync(); } - await _howl.Seek(soundId.Value, time); + await _howl.Seek(_currentPlayingSoundId.Value, time); } } @@ -180,27 +180,26 @@ public void Dispose() protected virtual void Dispose(bool disposing) { - if (!disposedValue) + if (!_disposedValue) { if (disposing) { - cuesheet.AudiofileChanged -= Cuesheet_AudiofileChanged; - _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; _howl.OnPlay -= Howl_OnPlay; _howl.OnPause -= Howl_OnPause; _howl.OnEnd -= Howl_OnEnd; _howl.OnStop -= Howl_OnStop; } - disposedValue = true; + _disposedValue = true; } } private void Howl_OnStop(Howler.Blazor.Components.Events.HowlEventArgs obj) { IsPlaying = false; - soundId = null; + _currentPlayingSoundId = null; StopTimer(); CurrentPosition = null; + _currentlyPlayingAudiofile = _sessionStateContainer.Cuesheet.Audiofile; } private void Howl_OnEnd(Howler.Blazor.Components.Events.HowlEventArgs obj) @@ -224,36 +223,27 @@ private void Howl_OnPlay(Howler.Blazor.Components.Events.HowlPlayEventArgs obj) private void StartTimer() { - updateTimer ??= new Timer(UpdateCurrentPosition, null, 0, 500); + _updateTimer ??= new Timer(UpdateCurrentPosition, null, 0, 500); } private void StopTimer() { - updateTimer?.Dispose(); - updateTimer = null; + _updateTimer?.Dispose(); + _updateTimer = null; } private async void UpdateCurrentPosition(object? state) { // Thread-safe access - lock (timerLock) + lock (_timerLock) { - if (soundId == null || !IsPlaying) return; + if (_currentPlayingSoundId == null || !IsPlaying) return; + } + CurrentPosition = await _howl.GetCurrentTime(_currentPlayingSoundId.Value); + if (_sessionStateContainer.Cuesheet.Audiofile != _currentlyPlayingAudiofile) + { + await _howl.Stop(_currentPlayingSoundId.Value); } - CurrentPosition = await _howl.GetCurrentTime(soundId.Value); - } - - private void SessionStateContainer_CuesheetChanged(object? sender, EventArgs e) - { - _ = StopAsync(); - cuesheet.AudiofileChanged -= Cuesheet_AudiofileChanged; - cuesheet = _sessionStateContainer.Cuesheet; - cuesheet.AudiofileChanged += Cuesheet_AudiofileChanged; - } - - private void Cuesheet_AudiofileChanged(object? sender, EventArgs e) - { - _ = StopAsync(); } } } diff --git a/AudioCuesheetEditor/Services/AudioCuesheet/CuesheetManager.cs b/AudioCuesheetEditor/Services/AudioCuesheet/CuesheetManager.cs new file mode 100644 index 00000000..6f45baa8 --- /dev/null +++ b/AudioCuesheetEditor/Services/AudioCuesheet/CuesheetManager.cs @@ -0,0 +1,279 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Services.UI; +using System.Linq.Expressions; +using System.Reflection; + +namespace AudioCuesheetEditor.Services.AudioCuesheet +{ + /// <inheritdoc/> + public class CuesheetManager(ITraceChangeManager traceChangeManager, ISessionStateContainer sessionStateContainer, ITrackManager trackManager) : ICuesheetManager + { + private readonly ITraceChangeManager _traceChangeManager = traceChangeManager; + private readonly ISessionStateContainer _sessionStateContainer = sessionStateContainer; + private readonly ITrackManager _trackManager = trackManager; + + public event EventHandler? IsRecordingChanged; + + /// <inheritdoc/> + public void SetProperty<TProperty>(Expression<Func<Cuesheet, TProperty>> propertyExpression, TProperty value) + { + _traceChangeManager.BulkEdit = true; + var cuesheet = _sessionStateContainer.GetActiveCuesheet(); + var audiofile = cuesheet?.Audiofile; + SetValue(cuesheet!, propertyExpression, value); + // If audiofile has been set, we need to calculate last track end + if (audiofile != cuesheet?.Audiofile) + { + SetLastTrackEnd(cuesheet!); + } + _traceChangeManager.BulkEdit = false; + } + + /// <inheritdoc/> + public Result IsRecordingPossible + { + get + { + if (_sessionStateContainer.Cuesheet.Tracks.Any()) + { + return Result.Failure(new Error(ErrorType.NotPossible, "Cuesheet already contains tracks!")); + } + return Result.Success(); + } + } + + /// <inheritdoc/> + public Result StartRecording() + { + var isRecordingPossibleResult = IsRecordingPossible; + if (isRecordingPossibleResult.IsSuccess) + { + var cuesheet = _sessionStateContainer.Cuesheet; + if (cuesheet.IsRecording == true) + { + return Result.Failure(new Error(ErrorType.NotPossible, "Record is already running!")); + } + cuesheet.RecordingStart = DateTime.UtcNow; + IsRecordingChanged?.Invoke(this, EventArgs.Empty); + return Result.Success(); + } + return isRecordingPossibleResult; + } + + /// <inheritdoc/> + public void StopRecording() + { + var cuesheet = _sessionStateContainer.Cuesheet; + if (cuesheet.IsRecording == true) + { + var lastTrack = cuesheet.Tracks.LastOrDefault(); + if ((lastTrack != null) && cuesheet.RecordingStart.HasValue) + { + lastTrack.End = DateTime.UtcNow - cuesheet.RecordingStart.Value; + } + cuesheet.RecordingStart = null; + IsRecordingChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// <inheritdoc/> + public void AddTrack(Track track) + { + var cuesheet = _sessionStateContainer.GetActiveCuesheet(); + track.Cuesheet = cuesheet; + // Calculate track properties + _traceChangeManager.BulkEdit = true; + if (cuesheet?.IsRecording == true) + { + _trackManager.SetProperty(track, x => x.Begin, DateTime.UtcNow - cuesheet.RecordingStart); + } + if (cuesheet?.Tracks.Any() == false) + { + _trackManager.SetProperty(track, x => x.Position, (ushort)(1)); + if ((track.Begin.HasValue == false) || cuesheet.IsRecording) + { + _trackManager.SetProperty(track, x => x.Begin, TimeSpan.Zero); + } + } + else + { + var lastTrack = GetLastTrack(cuesheet!); + if ((cuesheet?.Audiofile?.Duration.HasValue == true) && (lastTrack?.End.HasValue == true) && (lastTrack.End == cuesheet.Audiofile.Duration)) + { + _trackManager.SetProperty(lastTrack, x => x.End, null); + } + if (track.Position.HasValue == false) + { + _trackManager.SetProperty(track, x => x.Position, (ushort?)(lastTrack?.Position + 1)); + } + if (track.Begin.HasValue == false) + { + _trackManager.SetProperty(track, x => x.Begin, lastTrack?.End); + } + else + { + if (lastTrack?.End.HasValue == false) + { + _trackManager.SetProperty(lastTrack, x => x.End, track.Begin); + } + } + if (cuesheet?.IsRecording == true && lastTrack != null) + { + _trackManager.SetProperty(lastTrack, x => x.End, track.Begin); + } + } + var newValue = new List<Track>(cuesheet!.Tracks) + { + track + }; + SetValue(cuesheet, x => x.Tracks, newValue); + SetLastTrackEnd(cuesheet); + _traceChangeManager.BulkEdit = false; + } + + /// <inheritdoc/> + public void RemoveTracks(IEnumerable<Track> tracksToRemove) + { + var cuesheet = _sessionStateContainer.GetActiveCuesheet(); + var intersection = cuesheet!.Tracks.Intersect(tracksToRemove); + ICollection<Track> newValue = [.. cuesheet.Tracks.Except(intersection)]; + //Calculate position and begin of new tracks + ushort position = 1; + foreach (var track in newValue.OrderBy(x => x.Position)) + { + track.Position = position; + position++; + var previousTrack = _trackManager.GetPreviousLinkedTrack(track); + if (previousTrack?.End.HasValue == true) + { + track.Begin = previousTrack.End; + } + } + _traceChangeManager.BulkEdit = true; + SetValue(cuesheet, x => x.Tracks, newValue); + SetLastTrackEnd(cuesheet); + _traceChangeManager.BulkEdit = false; + } + + /// <inheritdoc/> + public bool IsMoveTracksUpPossible(HashSet<Track> selectedTracks) => selectedTracks.Count > 0 && selectedTracks.Min(x => x.Position) >= 2; + + /// <inheritdoc/> + public bool IsMoveTracksDownPossible(HashSet<Track> selectedTracks) => selectedTracks.Count > 0 && selectedTracks.Max(x => x.Position) < _sessionStateContainer.GetActiveCuesheet()?.Tracks.Max(x => x.Position); + + /// <inheritdoc/> + public Result MoveTracksUp(HashSet<Track> selectedTracks) + { + if (IsMoveTracksUpPossible(selectedTracks) == false) + { + return Result.Failure(new Error(ErrorType.NotPossible, "Moving tracks up is not possible!")); + } + _traceChangeManager.BulkEdit = true; + var cuesheet = _sessionStateContainer.GetActiveCuesheet(); + foreach (var selectedTrack in selectedTracks.OrderBy(x => x.Position)) + { + var previousTrack = cuesheet?.Tracks.FirstOrDefault(x => x.Position == selectedTrack.Position - 1); + var newBegin = previousTrack?.Begin; + var newEnd = previousTrack?.End; + if (previousTrack != null) + { + _trackManager.SetProperty(previousTrack, x => x.Position, selectedTrack.Position); + _trackManager.SetProperty(previousTrack, x => x.Begin, selectedTrack.Begin); + _trackManager.SetProperty(previousTrack, x => x.End, selectedTrack.End); + } + _trackManager.SetProperty(selectedTrack, x => x.Position, (ushort?)(selectedTrack.Position - 1)); + _trackManager.SetProperty(selectedTrack, x => x.Begin, newBegin); + _trackManager.SetProperty(selectedTrack, x => x.End, newEnd); + } + SetValue(cuesheet!, x => x.Tracks, cuesheet?.Tracks.OrderBy(x => x.Position)); + _traceChangeManager.BulkEdit = false; + return Result.Success(); + } + + /// <inheritdoc/> + public Result MoveTracksDown(HashSet<Track> selectedTracks) + { + var cuesheet = _sessionStateContainer.GetActiveCuesheet(); + if (IsMoveTracksDownPossible(selectedTracks) == false) + { + return Result.Failure(new Error(ErrorType.NotPossible, "Moving tracks down is not possible!")); + } + _traceChangeManager.BulkEdit = true; + foreach (var selectedTrack in selectedTracks.OrderByDescending(x => x.Position)) + { + var nextTrack = cuesheet?.Tracks.FirstOrDefault(x => x.Position == selectedTrack.Position + 1); + var newBegin = nextTrack?.Begin; + var newEnd = nextTrack?.End; + if (nextTrack != null) + { + _trackManager.SetProperty(nextTrack, x => x.Position, selectedTrack.Position); + _trackManager.SetProperty(nextTrack, x => x.Begin, selectedTrack.Begin); + _trackManager.SetProperty(nextTrack, x => x.End, selectedTrack.End); + } + var newPosition = (ushort?)(selectedTrack.Position + 1); + _trackManager.SetProperty(selectedTrack, x => x.Position, newPosition); + _trackManager.SetProperty(selectedTrack, x => x.Begin, newBegin); + _trackManager.SetProperty(selectedTrack, x => x.End, newEnd); + } + SetValue(cuesheet!, x => x.Tracks, cuesheet?.Tracks.OrderBy(x => x.Position)); + _traceChangeManager.BulkEdit = false; + return Result.Success(); + } + + void SetValue<TProperty>(Cuesheet cuesheet, Expression<Func<Cuesheet, TProperty>> propertyExpression, TProperty value) + { + if (propertyExpression.Body is not MemberExpression memberExpression) + { + throw new ArgumentException("Expression must be a property"); + } + + if (memberExpression.Member is not PropertyInfo propertyInfo) + { + throw new ArgumentException("Member is not a property"); + } + + var previousValue = (TProperty?)propertyInfo.GetValue(cuesheet); + if (Equals(previousValue, value)) + { + return; + } + + propertyInfo.SetValue(cuesheet, value); + + _traceChangeManager.AddChange(new(cuesheet, new(previousValue, propertyInfo.Name))); + } + + void SetLastTrackEnd(Cuesheet cuesheet) + { + var lastTrack = GetLastTrack(cuesheet); + if ((lastTrack?.End.HasValue == false) && (cuesheet.Audiofile?.Duration.HasValue == true)) + { + _trackManager.SetProperty(lastTrack, x => x.End, cuesheet.Audiofile.Duration); + } + } + + static Track? GetLastTrack(Cuesheet cuesheet) + { + return cuesheet.Tracks + .OrderByDescending(x => x.Position.HasValue).ThenBy(x => x.Position) + .ThenByDescending(x => x.Begin.HasValue).ThenBy(x => x.Begin) + .ThenByDescending(x => x.End.HasValue).ThenBy(x => x.End) + .LastOrDefault(); + } + } +} diff --git a/AudioCuesheetEditor/Services/AudioCuesheet/ICuesheetManager.cs b/AudioCuesheetEditor/Services/AudioCuesheet/ICuesheetManager.cs new file mode 100644 index 00000000..460264ff --- /dev/null +++ b/AudioCuesheetEditor/Services/AudioCuesheet/ICuesheetManager.cs @@ -0,0 +1,86 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Model.AudioCuesheet; +using System.Linq.Expressions; + +namespace AudioCuesheetEditor.Services.AudioCuesheet +{ + /// <summary> + /// Manager for cuesheet interactions + /// </summary> + public interface ICuesheetManager + { + /// <summary> + /// Event propagated when recording on a cuesheet changes + /// </summary> + public event EventHandler? IsRecordingChanged; + /// <summary> + /// Set property for cuesheet + /// </summary> + /// <typeparam name="TProperty"></typeparam> + /// <param name="propertyExpression"></param> + /// <param name="value"></param> + void SetProperty<TProperty>(Expression<Func<Cuesheet, TProperty>> propertyExpression, TProperty value); + /// <summary> + /// Checks if recording for the cuesheet is possible + /// </summary> + /// <returns></returns> + Result IsRecordingPossible { get; } + /// <summary> + /// Starts recording on cuesheet + /// </summary> + /// <returns></returns> + Result StartRecording(); + /// <summary> + /// Stop recording on cuesheet + /// </summary> + /// <returns></returns> + void StopRecording(); + /// <summary> + /// Adds a track to the cuesheet + /// </summary> + /// <param name="track"></param> + void AddTrack(Track track); + /// <summary> + /// Remove tracks from cuesheet + /// </summary> + /// <param name="tracksToRemove"></param> + void RemoveTracks(IEnumerable<Track> tracksToRemove); + /// <summary> + /// Determines if moving tracks up is possible + /// </summary> + /// <param name="selectedTracks"></param> + /// <returns></returns> + Boolean IsMoveTracksUpPossible(HashSet<Track> selectedTracks); + /// <summary> + /// Determines if moving tracks down is possible + /// </summary> + /// <param name="selectedTracks"></param> + /// <returns></returns> + Boolean IsMoveTracksDownPossible(HashSet<Track> selectedTracks); + /// <summary> + /// Moves selected tracks up + /// </summary> + /// <param name="selectedTracks"></param> + Result MoveTracksUp(HashSet<Track> selectedTracks); + /// <summary> + /// Moves selected tracks down + /// </summary> + /// <param name="selectedTracks"></param> + /// <returns></returns> + Result MoveTracksDown(HashSet<Track> selectedTracks); + } +} diff --git a/AudioCuesheetEditor/Services/AudioCuesheet/ITrackManager.cs b/AudioCuesheetEditor/Services/AudioCuesheet/ITrackManager.cs new file mode 100644 index 00000000..fc631edd --- /dev/null +++ b/AudioCuesheetEditor/Services/AudioCuesheet/ITrackManager.cs @@ -0,0 +1,74 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Model.AudioCuesheet; +using System.Linq.Expressions; + +namespace AudioCuesheetEditor.Services.AudioCuesheet +{ + /// <summary> + /// Manages track interactions + /// </summary> + public interface ITrackManager + { + /// <summary> + /// Set property value for track + /// </summary> + /// <typeparam name="TProperty"></typeparam> + /// <param name="track"></param> + /// <param name="propertyExpression"></param> + /// <param name="value"></param> + void SetProperty<TProperty>(Track track, Expression<Func<Track, TProperty>> propertyExpression, TProperty value); + /// <summary> + /// Copies values from a track to another + /// </summary> + /// <param name="source"></param> + /// <param name="target"></param> + /// <param name="setIsLinkedToPreviousTrack"></param> + /// <param name="setPosition"></param> + /// <param name="setArtist"></param> + /// <param name="setTitle"></param> + /// <param name="setBegin"></param> + /// <param name="setEnd"></param> + /// <param name="setLength"></param> + /// <param name="setFlags"></param> + /// <param name="setPreGap"></param> + /// <param name="setPostGap"></param> + void CopyValues(ITrack source, Track target, Boolean setIsLinkedToPreviousTrack = true, Boolean setPosition = true, Boolean setArtist = true, Boolean setTitle = true, Boolean setBegin = true, Boolean setEnd = true, Boolean setLength = true, Boolean setFlags = true, Boolean setPreGap = true, Boolean setPostGap = true); + /// <summary> + /// Create a clone of a track + /// </summary> + /// <param name="track"></param> + /// <returns></returns> + Track Clone(ITrack track); + /// <summary> + /// Get the previous linked track of a track object + /// </summary> + /// <param name="track">Track object to get the previous link to</param> + /// <returns>Previous linked track or null (if not linked)</returns> + Track? GetPreviousLinkedTrack(Track track); + /// <summary> + /// Get next linked track that is linked to the parameter track + /// </summary> + /// <param name="track"></param> + /// <returns></returns> + Track? GetNextLinkedTrack(Track track); + /// <summary> + /// Recalculates properties of linked tracks (previous and next track) + /// </summary> + /// <param name="track"></param> + void RecalculateLinkedTracksProperties(Track track); + } +} diff --git a/AudioCuesheetEditor/Services/AudioCuesheet/TrackManager.cs b/AudioCuesheetEditor/Services/AudioCuesheet/TrackManager.cs new file mode 100644 index 00000000..cbffeb9b --- /dev/null +++ b/AudioCuesheetEditor/Services/AudioCuesheet/TrackManager.cs @@ -0,0 +1,193 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Services.UI; +using System.Linq.Expressions; +using System.Reflection; + +namespace AudioCuesheetEditor.Services.AudioCuesheet +{ + /// <inheritdoc/> + public class TrackManager(ITraceChangeManager traceChangeManager) : ITrackManager + { + private readonly ITraceChangeManager _traceChangeManager = traceChangeManager; + + /// <inheritdoc/> + public void SetProperty<TProperty>(Track track, Expression<Func<Track, TProperty>> propertyExpression, TProperty value) + { + SetValue(track, propertyExpression, value); + } + + /// <inheritdoc/> + public Track Clone(ITrack track) + { + Boolean setLength = true; + if (track.Begin.HasValue && track.End.HasValue) + { + setLength = false; + } + var clone = new Track() + { + IsLinkedToPreviousTrack = track.IsLinkedToPreviousTrack, + Position = track.Position, + Artist = track.Artist, + Title = track.Title, + Begin = track.Begin, + End = track.End, + Flags = track.Flags, + PreGap = track.PreGap, + PostGap = track.PostGap + }; + if (setLength) + { + clone.Length = track.Length; + } + return clone; + } + + /// <inheritdoc/> + public void CopyValues(ITrack source, Track target, bool setIsLinkedToPreviousTrack = true, bool setPosition = true, bool setArtist = true, bool setTitle = true, bool setBegin = true, bool setEnd = true, bool setLength = true, bool setFlags = true, bool setPreGap = true, bool setPostGap = true) + { + if (setIsLinkedToPreviousTrack) + { + SetValue(target, x => x.IsLinkedToPreviousTrack, source.IsLinkedToPreviousTrack); + } + if (setPosition) + { + SetValue(target, x => x.Position, source.Position); + } + if (setArtist) + { + SetValue(target, x => x.Artist, source.Artist); + } + if (setTitle) + { + SetValue(target, x => x.Title, source.Title); + } + if (setBegin) + { + SetValue(target, x => x.Begin, source.Begin ); + } + if (setEnd) + { + SetValue(target, x => x.End, source.End); + } + if (setLength) + { + SetValue(target, x => x.Length, source.Length); + } + if (setFlags) + { + SetValue(target, x => x.Flags, source.Flags); + } + if (setPreGap) + { + SetValue(target, x => x.PreGap, source.PreGap); + } + if (setPostGap) + { + SetValue(target, x=> x.PostGap, source.PostGap); + } + } + + /// <inheritdoc/> + public Track? GetPreviousLinkedTrack(Track track) + { + if (track.IsLinkedToPreviousTrack == false) + { + return null; + } + if (track.Position.HasValue && (track.Cuesheet?.Tracks.All(x => x.Position.HasValue) == true)) + { + return track.Cuesheet?.Tracks.LastOrDefault(x => x.Position == track.Position - 1 && Equals(x, track) == false); + } + if (track.Begin.HasValue) + { + return track.Cuesheet?.Tracks.OrderBy(x => x.End).LastOrDefault(x => x.End <= track.Begin && Equals(x, track) == false); + } + return null; + } + + /// <inheritdoc/> + public Track? GetNextLinkedTrack(Track track) + { + if (track.Position.HasValue && (track.Cuesheet?.Tracks.All(x => x.Position.HasValue) == true)) + { + return track.Cuesheet?.Tracks.OrderBy(x => x.Begin).FirstOrDefault(x => x.Position >= track.Position.Value + 1 && x.IsLinkedToPreviousTrack == true && Equals(x, track) == false); + } + if (track.End.HasValue) + { + return track.Cuesheet?.Tracks.OrderBy(x => x.Begin).LastOrDefault(x => x.Begin <= track.End && x.IsLinkedToPreviousTrack == true && Equals(x, track) == false); + } + return null; + } + + /// <inheritdoc/> + public void RecalculateLinkedTracksProperties(Track track) + { + if (track.Cuesheet != null) + { + var previousTrack = GetPreviousLinkedTrack(track); + if (previousTrack != null) + { + if (track.Position.HasValue == false && previousTrack.Position.HasValue && (track.Position != previousTrack.Position.Value + 1)) + { + SetValue(track, x => x.Position, (ushort?)(previousTrack.Position + 1)); + } + if (previousTrack.End.HasValue && (track.Begin != previousTrack.End)) + { + SetValue(track, x => x.Begin, previousTrack.End); + } + if ((previousTrack.End.HasValue == false) && track.Begin.HasValue) + { + SetValue(previousTrack, x => x.End, track.Begin); + } + } + var nextTrack = GetNextLinkedTrack(track); + if (nextTrack != null) + { + if (track.Position.HasValue) + { + SetValue(nextTrack, x => x.Position, (ushort?)(track.Position + 1)); + } + SetValue(nextTrack, x => x.Begin, track.End); + } + } + } + + void SetValue<TProperty>(Track track, Expression<Func<Track, TProperty>> propertyExpression, TProperty value) + { + if (propertyExpression.Body is not MemberExpression memberExpression) + { + throw new ArgumentException("Expression must be a property"); + } + + if (memberExpression.Member is not PropertyInfo propertyInfo) + { + throw new ArgumentException("Member is not a property"); + } + + var previousValue = (TProperty?)propertyInfo.GetValue(track); + if (Equals(previousValue, value)) + { + return; + } + + propertyInfo.SetValue(track, value); + _traceChangeManager.AddChange(new(track, new(previousValue, propertyInfo.Name))); + } + } +} \ No newline at end of file diff --git a/AudioCuesheetEditor/Services/IO/ImportManager.cs b/AudioCuesheetEditor/Services/IO/ImportManager.cs index adeabb5e..1a17a2dc 100644 --- a/AudioCuesheetEditor/Services/IO/ImportManager.cs +++ b/AudioCuesheetEditor/Services/IO/ImportManager.cs @@ -18,6 +18,8 @@ using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Services.AudioCuesheet; using AudioCuesheetEditor.Services.UI; using System.Diagnostics; @@ -31,7 +33,7 @@ public enum ImportFileType Textfile, Audiofile } - public class ImportManager(ISessionStateContainer sessionStateContainer, ITraceChangeManager traceChangeManager, IFileInputManager fileInputManager, ITextImportService textImportService, ILogger<ImportManager> logger) + public class ImportManager(ISessionStateContainer sessionStateContainer, ITraceChangeManager traceChangeManager, IFileInputManager fileInputManager, ITextImportService textImportService, ITrackManager trackManager, ILogger<ImportManager> logger) { public event EventHandler<IEnumerable<string>>? UploadFilesFinished; @@ -40,6 +42,7 @@ public class ImportManager(ISessionStateContainer sessionStateContainer, ITraceC private readonly ITraceChangeManager _traceChangeManager = traceChangeManager; private readonly IFileInputManager _fileInputManager = fileInputManager; private readonly ITextImportService _textImportService = textImportService; + private readonly ITrackManager _trackManager = trackManager; public void ImportData(String? data) { @@ -67,7 +70,10 @@ public async Task AnalyseImportfile() switch (_sessionStateContainer.Importfile?.FileType) { case ImportFileType.ProjectFile: - _sessionStateContainer.Cuesheet = Projectfile.ImportFile(fileContent)!; + var importedCuesheet = Projectfile.ImportFile(fileContent); + var previousValue = _sessionStateContainer.Cuesheet; + _sessionStateContainer.Cuesheet = importedCuesheet!; + _traceChangeManager.AddChange(new TracedChange(_sessionStateContainer, new(previousValue, nameof(SessionStateContainer.Cuesheet)))); break; case ImportFileType.Textfile: _sessionStateContainer.Importfile = await _textImportService.AnalyseAsync(fileContent); @@ -81,24 +87,18 @@ public async Task AnalyseImportfile() { switch (_sessionStateContainer.Importfile?.FileType) { + case ImportFileType.Cuesheet: case ImportFileType.Textfile: var importCuesheet = new Cuesheet(); CopyCuesheet(importCuesheet, _sessionStateContainer.Importfile.AnalyzedCuesheet); _sessionStateContainer.ImportCuesheet = importCuesheet; - StartTracing(); - break; - case ImportFileType.Cuesheet: - _traceChangeManager.BulkEdit = true; - _sessionStateContainer.Cuesheet = new(); - CopyCuesheet(_sessionStateContainer.Cuesheet, _sessionStateContainer.Importfile.AnalyzedCuesheet); - _traceChangeManager.BulkEdit = false; break; } } stopwatch.Stop(); if (_logger.IsEnabled(LogLevel.Debug)) { - _logger.LogDebug("ImportTextAsync duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + _logger.LogDebug("AnalyseImportfile duration: {stopwatch.Elapsed}", stopwatch.Elapsed); } } @@ -108,9 +108,11 @@ public void ImportCuesheet() ResetTracing(); if (_sessionStateContainer.ImportCuesheet != null) { - _traceChangeManager.BulkEdit = true; - CopyCuesheet(_sessionStateContainer.Cuesheet, _sessionStateContainer.ImportCuesheet); - _traceChangeManager.BulkEdit = false; + var newCuesheet = _sessionStateContainer.ImportCuesheet; + CopyCuesheet(newCuesheet, _sessionStateContainer.ImportCuesheet); + var previousValue = _sessionStateContainer.Cuesheet; + _sessionStateContainer.Cuesheet = newCuesheet; + _traceChangeManager.AddChange(new TracedChange(_sessionStateContainer, new(previousValue, nameof(SessionStateContainer.Cuesheet)))); } _sessionStateContainer.ResetImport(); stopwatch.Stop(); @@ -177,9 +179,8 @@ public async Task UploadFilesAsync(IEnumerable<FileUpload> files) } } - private static void CopyCuesheet(Cuesheet target, ICuesheet cuesheetToCopy) + private void CopyCuesheet(Cuesheet target, ICuesheet cuesheetToCopy) { - target.IsImporting = true; target.Artist = cuesheetToCopy.Artist; target.Title = cuesheetToCopy.Title; target.Cataloguenumber = cuesheetToCopy.Cataloguenumber; @@ -205,49 +206,74 @@ private static void CopyCuesheet(Cuesheet target, ICuesheet cuesheetToCopy) } if (tracks != null) { - var begin = TimeSpan.Zero; - for (int i = 0; i < tracks.Count(); i++) + IOrderedEnumerable<ITrack> sortedTracks; + if (tracks.All(x => x.Position.HasValue)) + { + sortedTracks = tracks.OrderBy(x => x.Position); + } + else + { + sortedTracks = tracks.OrderByDescending(x => x.Position.HasValue).ThenBy(x => x.Position); + } + if (sortedTracks.All(x => x.Begin.HasValue)) + { + sortedTracks = sortedTracks.ThenBy(x => x.Begin); + } + else + { + sortedTracks = sortedTracks.ThenByDescending(x => x.Begin.HasValue).ThenBy(x => x.Begin); + } + if (sortedTracks.All(x => x.End.HasValue)) + { + sortedTracks = sortedTracks.ThenBy(x => x.End); + } + else + { + sortedTracks = sortedTracks.ThenByDescending(x => x.End.HasValue).ThenBy(x => x.End); + } + List<Track> targetTracks = []; + TimeSpan? begin = TimeSpan.Zero; + ushort position = 1; + foreach (var (importTrack, index) in sortedTracks.Select((track, i) => (track, i))) { - var importTrack = tracks.ElementAt(i); - //We don't want to copy the cuesheet reference since we are doing a copy and want to assign the track to this object - var track = new Track(importTrack, false); - if (importTrack is ImportTrack importTrackReference) + ITrack? nextTrack = null; + if (index < sortedTracks.Count() - 1) { - if (importTrackReference.StartDateTime != null) - { - if (i < tracks.Count() - 1) - { - var nextTrack = (ImportTrack)tracks.ElementAt(i + 1); - var length = nextTrack.StartDateTime - importTrackReference.StartDateTime; - track.Begin = begin; - track.End = begin + length; - if (track.End.HasValue) - { - begin = track.End.Value; - } - } - } + nextTrack = sortedTracks.ElementAt(index + 1); + } + // Copy track + var track = _trackManager.Clone(importTrack); + track.Cuesheet = target; + // Special treatment for StartDateTime of ImportTrack + if (importTrack is ImportTrack importTrackReference && importTrackReference.StartDateTime != null && nextTrack is ImportTrack nextImportTrackReference) + { + var length = nextImportTrackReference.StartDateTime - importTrackReference.StartDateTime; + track.Begin = begin; + track.End = begin + length; + } + // Calculate properties + if (track.Position.HasValue == false) + { + track.Position = position; + } + if (track.Begin.HasValue == false) + { + track.Begin = begin; + } + if ((track.End.HasValue == false) && (nextTrack?.Begin.HasValue == true)) + { + track.End = nextTrack.Begin; } - target.AddTrack(track); + begin = track.End; + position++; + targetTracks.Add(track); } + target.Tracks = targetTracks; } else { throw new NullReferenceException(); } - target.IsImporting = false; - } - - private void StartTracing() - { - if (_sessionStateContainer.ImportCuesheet != null) - { - _traceChangeManager.TraceChanges(_sessionStateContainer.ImportCuesheet); - foreach (var track in _sessionStateContainer.ImportCuesheet.Tracks) - { - _traceChangeManager.TraceChanges(track); - } - } } private void ResetTracing() diff --git a/AudioCuesheetEditor/Services/IO/TextImportService.cs b/AudioCuesheetEditor/Services/IO/TextImportService.cs index 63245326..ab975076 100644 --- a/AudioCuesheetEditor/Services/IO/TextImportService.cs +++ b/AudioCuesheetEditor/Services/IO/TextImportService.cs @@ -199,9 +199,9 @@ private static void SetValue(object entity, PropertyInfo property, string value, { property.SetValue(entity, TimeSpanUtility.ParseTimeSpan(value, timeSpanFormat)); } - if (property.PropertyType == typeof(uint?)) + if (property.PropertyType == typeof(ushort?)) { - property.SetValue(entity, Convert.ToUInt32(value)); + property.SetValue(entity, Convert.ToUInt16(value)); } if (property.PropertyType == typeof(String)) { @@ -255,7 +255,7 @@ private static Regex CreateCuesheetRegexPattern(string scheme) nextFieldIdx = pos; } } - string separator = scheme.Substring(idx, nextFieldIdx - idx); + string separator = scheme[idx..nextFieldIdx]; parts.Add(separator); idx = nextFieldIdx; } @@ -324,7 +324,7 @@ private static Regex CreateTrackRegexPattern(string scheme) nextFieldIdx = pos; } } - string separator = scheme.Substring(idx, nextFieldIdx - idx); + string separator = scheme[idx..nextFieldIdx]; parts.Add(separator); idx = nextFieldIdx; } diff --git a/AudioCuesheetEditor/Services/Result.cs b/AudioCuesheetEditor/Services/Result.cs index da870084..fac41830 100644 --- a/AudioCuesheetEditor/Services/Result.cs +++ b/AudioCuesheetEditor/Services/Result.cs @@ -23,7 +23,15 @@ public enum ErrorType /// <summary> /// Validation failed with error /// </summary> - ValidationFailed + ValidationFailed, + /// <summary> + /// Resource already exists + /// </summary> + AlreadyDone, + /// <summary> + /// Current action is not possible + /// </summary> + NotPossible } /// <summary> diff --git a/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs b/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs index 6317037b..d23c44ac 100644 --- a/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs +++ b/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs @@ -54,6 +54,13 @@ public async Task TimespanTextChanged<T, TProperty>(T entity, Expression<Func<T, } } + public async Task<TimeSpan?> GetTimespanAsync(String value) + { + await EnsureInitializedAsync(); + TimeSpan? result = TimeSpanUtility.ParseTimeSpan(value, applicationOptions?.TimeSpanFormat); + return result; + } + public string? GetTimespanFormatted(TimeSpan? timeSpan) { string? formatted = null; diff --git a/AudioCuesheetEditor/Services/UI/DialogManager.cs b/AudioCuesheetEditor/Services/UI/DialogManager.cs index 6c59f2dd..16b48a45 100644 --- a/AudioCuesheetEditor/Services/UI/DialogManager.cs +++ b/AudioCuesheetEditor/Services/UI/DialogManager.cs @@ -15,35 +15,38 @@ //<http: //www.gnu.org/licenses />. using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Services.AudioCuesheet; using AudioCuesheetEditor.Shared.Dialogs; using MudBlazor; namespace AudioCuesheetEditor.Services.UI { - public class DialogManager(IDialogService dialogService, ITraceChangeManager traceChangeManager) + public class DialogManager(IDialogService dialogService, ITraceChangeManager traceChangeManager, ITrackManager trackManager) { private readonly IDialogService _dialogService = dialogService; private readonly ITraceChangeManager _traceChangeManager = traceChangeManager; + private readonly ITrackManager _trackManager = trackManager; - private IDialogReference? loadingDialog; + private IDialogReference? _loadingDialog; public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) { _traceChangeManager.BulkEdit = true; if (tracks.Count() == 1) { - var parameters = new DialogParameters<EditTrackModal> { { x => x.EditedTrack, tracks.First().Clone() } }; + var parameters = new DialogParameters<EditTrackModal> { { x => x.EditedTrack, _trackManager.Clone(tracks.First()) } }; var options = new DialogOptions() { CloseOnEscapeKey = true, BackdropClick = false, FullWidth = true, CloseButton = true }; var dialog = await _dialogService.ShowAsync<EditTrackModal>(null, parameters, options); var result = await dialog.Result; if ((result?.Canceled == false) && (result.Data is Track editedTrack)) { - tracks.First().CopyValues(editedTrack, setCuesheet: false); + _trackManager.CopyValues(editedTrack, tracks.First()); + _trackManager.RecalculateLinkedTracksProperties(tracks.First()); } } if (tracks.Count() > 1) { - var parameters = new DialogParameters<EditMultipleTracksModal> { { x => x.EditedTrack, new() { AutomaticallyCalculateLength = false } } }; + var parameters = new DialogParameters<EditMultipleTracksModal> { { x => x.EditedTrack, new() } }; var options = new DialogOptions() { CloseOnEscapeKey = true, BackdropClick = false, FullWidth = true, CloseButton = true }; var dialog = await _dialogService.ShowAsync<EditMultipleTracksModal>(null, parameters, options); var result = await dialog.Result; @@ -58,7 +61,6 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) var preGap = editMultipleTracksModalResult.EditedTrack.PreGap; var postGap = editMultipleTracksModalResult.EditedTrack.PostGap; Boolean copyIsLinkedToPreviousTrack = editMultipleTracksModalResult.IsLinkedToPreviousTrackEditMode == DynamicEditValue.EnteredValueEquals; - Boolean copyTrackPosition = true; Boolean copyTrackArtist = editMultipleTracksModalResult.ArtistEditMode == DynamicEditValue.EnteredValueEquals; Boolean copyTrackTitle = editMultipleTracksModalResult.TitleEditMode == DynamicEditValue.EnteredValueEquals; Boolean copyTrackBegin = true; @@ -68,27 +70,6 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) Boolean copyTrackPreGap = true; Boolean copyTrackPostGap = true; //First process dynamic edit, because we need to increase each value separately - switch (editMultipleTracksModalResult.PositionEditMode) - { - case DynamicEditValue.DoNotChange: - copyTrackPosition = false; - break; - case DynamicEditValue.EnteredValueEquals: - copyTrackPosition = true; - break; - case DynamicEditValue.EnteredValueAdd: - editMultipleTracksModalResult.EditedTrack.Position += track.Position; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: copyTrackPosition, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); - copyTrackPosition = false; - editMultipleTracksModalResult.EditedTrack.Position = position; - break; - case DynamicEditValue.EnteredValueSubstract: - var newValue = track.Position - editMultipleTracksModalResult.EditedTrack.Position; - editMultipleTracksModalResult.EditedTrack.Position = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: copyTrackPosition, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); - copyTrackPosition = false; - break; - } switch (editMultipleTracksModalResult.BeginEditMode) { case DynamicEditValue.DoNotChange: @@ -100,13 +81,13 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) case DynamicEditValue.EnteredValueAdd: var newValue = editMultipleTracksModalResult.EditedTrack.Begin + track.Begin; editMultipleTracksModalResult.EditedTrack.Begin = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: copyTrackBegin, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: copyTrackBegin, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: false); copyTrackBegin = false; break; case DynamicEditValue.EnteredValueSubstract: newValue = track.Begin - editMultipleTracksModalResult.EditedTrack.Begin; editMultipleTracksModalResult.EditedTrack.Begin = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: copyTrackBegin, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: copyTrackBegin, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: false); copyTrackBegin = false; break; } @@ -121,13 +102,13 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) case DynamicEditValue.EnteredValueAdd: var newValue = editMultipleTracksModalResult.EditedTrack.End + track.End; editMultipleTracksModalResult.EditedTrack.End = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: copyTrackEnd, setLength: false, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: copyTrackEnd, setLength: false, setFlags: false, setPreGap: false, setPostGap: false); copyTrackEnd = false; break; case DynamicEditValue.EnteredValueSubstract: newValue = track.End - editMultipleTracksModalResult.EditedTrack.End; editMultipleTracksModalResult.EditedTrack.End = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: copyTrackEnd, setLength: false, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: copyTrackEnd, setLength: false, setFlags: false, setPreGap: false, setPostGap: false); copyTrackEnd = false; break; } @@ -142,13 +123,13 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) case DynamicEditValue.EnteredValueAdd: var newValue = editMultipleTracksModalResult.EditedTrack.Length + track.Length; editMultipleTracksModalResult.EditedTrack.Length = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: copyTrackLength, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: copyTrackLength, setFlags: false, setPreGap: false, setPostGap: false); copyTrackLength = false; break; case DynamicEditValue.EnteredValueSubstract: newValue = track.Length - editMultipleTracksModalResult.EditedTrack.Length; editMultipleTracksModalResult.EditedTrack.Length = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: copyTrackLength, setFlags: false, setPreGap: false, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: copyTrackLength, setFlags: false, setPreGap: false, setPostGap: false); copyTrackLength = false; break; } @@ -163,13 +144,13 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) case DynamicEditValue.EnteredValueAdd: var newValue = editMultipleTracksModalResult.EditedTrack.PreGap + track.PreGap; editMultipleTracksModalResult.EditedTrack.PreGap = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: copyTrackPreGap, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: copyTrackPreGap, setPostGap: false); copyTrackPreGap = false; break; case DynamicEditValue.EnteredValueSubstract: newValue = track.PreGap - editMultipleTracksModalResult.EditedTrack.PreGap; editMultipleTracksModalResult.EditedTrack.PreGap = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: copyTrackPreGap, setPostGap: false, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: copyTrackPreGap, setPostGap: false); copyTrackPreGap = false; break; } @@ -184,13 +165,13 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) case DynamicEditValue.EnteredValueAdd: var newValue = editMultipleTracksModalResult.EditedTrack.PostGap + track.PostGap; editMultipleTracksModalResult.EditedTrack.PostGap = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: copyTrackPostGap, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: copyTrackPostGap); copyTrackPostGap = false; break; case DynamicEditValue.EnteredValueSubstract: newValue = track.PostGap - editMultipleTracksModalResult.EditedTrack.PostGap; editMultipleTracksModalResult.EditedTrack.PostGap = newValue; - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: copyTrackPostGap, useInternalSetters: Track.AllPropertyNames); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: false, setPosition: false, setArtist: false, setTitle: false, setBegin: false, setEnd: false, setLength: false, setFlags: false, setPreGap: false, setPostGap: copyTrackPostGap); copyTrackPostGap = false; break; } @@ -201,7 +182,7 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) editMultipleTracksModalResult.EditedTrack.PreGap = preGap; editMultipleTracksModalResult.EditedTrack.PostGap = postGap; //Now copy all values - track.CopyValues(editMultipleTracksModalResult.EditedTrack, setCuesheet: false, setIsLinkedToPreviousTrack: copyIsLinkedToPreviousTrack, setPosition: copyTrackPosition, setArtist: copyTrackArtist, setTitle: copyTrackTitle, setBegin: copyTrackBegin, setEnd: copyTrackEnd, setLength: copyTrackLength, setFlags: copyTrackFlags, setPreGap: copyTrackPreGap, setPostGap: copyTrackPostGap); + _trackManager.CopyValues(editMultipleTracksModalResult.EditedTrack, track, setIsLinkedToPreviousTrack: copyIsLinkedToPreviousTrack, setPosition: false, setArtist: copyTrackArtist, setTitle: copyTrackTitle, setBegin: copyTrackBegin, setEnd: copyTrackEnd, setLength: copyTrackLength, setFlags: copyTrackFlags, setPreGap: copyTrackPreGap, setPostGap: copyTrackPostGap); } } } @@ -210,21 +191,18 @@ public async Task ShowAndHandleModalEditDialogAsync(IEnumerable<Track> tracks) public async Task ShowLoadingDialogAsync() { - if (loadingDialog == null) + if (_loadingDialog == null) { var options = new DialogOptions() { BackdropClick = false, FullWidth = true, MaxWidth = MaxWidth.ExtraSmall, NoHeader = true }; - loadingDialog = await _dialogService.ShowAsync<LoadingDialog>(null, options); + _loadingDialog = await _dialogService.ShowAsync<LoadingDialog>(null, options); await Task.Delay(1); } } public void HideLoadingDialog() { - if (loadingDialog != null) - { - loadingDialog.Close(); - loadingDialog = null; - } + _loadingDialog?.Close(); + _loadingDialog = null; } } } diff --git a/AudioCuesheetEditor/Services/UI/ISessionStateContainer.cs b/AudioCuesheetEditor/Services/UI/ISessionStateContainer.cs index 4dcd0aca..e3f31f3f 100644 --- a/AudioCuesheetEditor/Services/UI/ISessionStateContainer.cs +++ b/AudioCuesheetEditor/Services/UI/ISessionStateContainer.cs @@ -27,7 +27,17 @@ public interface ISessionStateContainer public Cuesheet? ImportCuesheet { get; set; } public Audiofile? ImportAudiofile { get; set; } public IImportfile? Importfile { get; set; } - public void ResetImport(); public Boolean ImportIsAnalyzed { get; set; } + public void ResetImport(); + /// <summary> + /// Initialize this class + /// </summary> + /// <returns></returns> + Task InitializeAsync(); + /// <summary> + /// Get current cuesheet based on current view + /// </summary> + /// <returns></returns> + public Cuesheet? GetActiveCuesheet(); } } diff --git a/AudioCuesheetEditor/Services/UI/ITraceChangeManager.cs b/AudioCuesheetEditor/Services/UI/ITraceChangeManager.cs index 757b64bd..4aec1c11 100644 --- a/AudioCuesheetEditor/Services/UI/ITraceChangeManager.cs +++ b/AudioCuesheetEditor/Services/UI/ITraceChangeManager.cs @@ -1,4 +1,19 @@ -using AudioCuesheetEditor.Model.UI; +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Model.UI; namespace AudioCuesheetEditor.Services.UI { @@ -19,11 +34,14 @@ public interface ITraceChangeManager /// </summary> public bool CanRedo { get; } public bool BulkEdit { get; set; } - public void TraceChanges(ITraceable traceable); + /// <summary> + /// Adds a change of a property + /// </summary> + /// <param name="tracedChange"></param> + void AddChange(TracedChange tracedChange); public void Reset(); public void Undo(); public void Redo(); - public void MergeLastEditWithEdit(Func<TracedChanges, bool> targetEdit); - public void RemoveTracedChanges(IEnumerable<ITraceable> traceables); + public void RemoveTracedChanges(IEnumerable<object> traceables); } } diff --git a/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs b/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs index b1a7b0df..f4e00e55 100644 --- a/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs +++ b/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs @@ -13,72 +13,66 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //<http: //www.gnu.org/licenses />. +using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; -using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Model.Options; namespace AudioCuesheetEditor.Services.UI { - public class SessionStateContainer : ISessionStateContainer, ITraceable + public class SessionStateContainer : ISessionStateContainer, IDisposable { + private readonly ILocalStorageOptionsProvider _localStorageOptionsProvider; + + private Cuesheet _cuesheet = new(); + private Cuesheet? _importCuesheet; + private ViewOptions? _viewOptions; + private bool disposedValue; + public event EventHandler? CuesheetChanged; public event EventHandler? ImportCuesheetChanged; - public event EventHandler<TraceablePropertiesChangedEventArgs>? TraceablePropertyChanged; - - private readonly ITraceChangeManager _traceChangeManager; - private Cuesheet cuesheet; - private Cuesheet? importCuesheet; - private Audiofile? importAudiofile; - public SessionStateContainer(ITraceChangeManager traceChangeManager) + public SessionStateContainer(ILocalStorageOptionsProvider localStorageOptionsProvider) { - _traceChangeManager = traceChangeManager; - cuesheet = new Cuesheet(); - SetCuesheetReference(cuesheet); - _traceChangeManager.TraceChanges(this); + _localStorageOptionsProvider = localStorageOptionsProvider; + _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; } - public Cuesheet Cuesheet + + public void Dispose() { - get { return cuesheet; } - set { SetCuesheetReference(value); } + Dispose(disposing: true); + GC.SuppressFinalize(this); } - public Cuesheet? ImportCuesheet + + public Cuesheet Cuesheet { - get { return importCuesheet; } + get => _cuesheet; set { - var previousValue = importCuesheet; - importCuesheet = value; - //When there is an audiofile from import, we use this file because it has an object url and gets duration, etc. - if (importCuesheet != null && ImportAudiofile != null) - { - importCuesheet.Audiofile = ImportAudiofile; - } - if (Equals(previousValue, importCuesheet) == false) - { - ImportCuesheetChanged?.Invoke(this, EventArgs.Empty); - } + _cuesheet = value; + CuesheetChanged?.Invoke(this, EventArgs.Empty); } } - - public Audiofile? ImportAudiofile + public Cuesheet? ImportCuesheet { - get => importAudiofile; + get => _importCuesheet; set { - importAudiofile = value; - if (ImportCuesheet != null && ImportAudiofile != null) - { - ImportCuesheet.Audiofile = ImportAudiofile; - ImportCuesheetChanged?.Invoke(this, EventArgs.Empty); - } + _importCuesheet = value; + ImportCuesheetChanged?.Invoke(this, EventArgs.Empty); } } - + public Audiofile? ImportAudiofile { get; set; } public IImportfile? Importfile{ get; set; } public Boolean ImportIsAnalyzed { get; set; } = false; + /// <inheritdoc/> + public async Task InitializeAsync() + { + _viewOptions ??= await _localStorageOptionsProvider.GetOptionsAsync<ViewOptions>(); + } + public void ResetImport() { Importfile = null; @@ -86,13 +80,38 @@ public void ResetImport() ImportCuesheet = null; } - private void SetCuesheetReference(Cuesheet value) + /// <inheritdoc/> + public Cuesheet? GetActiveCuesheet() { - var previousValue = Cuesheet; - cuesheet = value; - _traceChangeManager.TraceChanges(Cuesheet); - TraceablePropertyChanged?.Invoke(this, new TraceablePropertiesChangedEventArgs(new TraceableChange(previousValue, nameof(Cuesheet)))); - CuesheetChanged?.Invoke(this, EventArgs.Empty); + if (_viewOptions == null) + { + throw new InvalidOperationException("Not initialized!"); + } + if (_viewOptions.ActiveTab == ViewMode.ImportView) + { + return ImportCuesheet; + } + return Cuesheet; + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + } + disposedValue = true; + } + } + + private void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions options) + { + if (options is ViewOptions viewOptions) + { + _viewOptions = viewOptions; + } } } } diff --git a/AudioCuesheetEditor/Services/UI/TraceChangeManager.cs b/AudioCuesheetEditor/Services/UI/TraceChangeManager.cs index 45e944db..79a53ef1 100644 --- a/AudioCuesheetEditor/Services/UI/TraceChangeManager.cs +++ b/AudioCuesheetEditor/Services/UI/TraceChangeManager.cs @@ -17,75 +17,30 @@ namespace AudioCuesheetEditor.Services.UI { - /// <summary> - /// Class for tracing changes on an object - /// </summary> - public class TracedChange(ITraceable traceableObject, TraceableChange traceableChange) - { - private readonly WeakReference<ITraceable> _tracedObject = new(traceableObject, false); - - public ITraceable? TraceableObject - { - get - { - if (_tracedObject.TryGetTarget(out var traceable)) - { - return traceable; - } - return null; - } - } - - public TraceableChange TraceableChange { get; } = traceableChange; - } - - public class TracedChanges(IEnumerable<TracedChange> changes) - { - public List<TracedChange> Changes { get; } = [.. changes]; - public bool HasTraceableObject { get { return Changes.Any(x => x.TraceableObject != null); } } - } - /// <inheritdoc /> public class TraceChangeManager(ILogger<TraceChangeManager> logger) : ITraceChangeManager { private readonly ILogger<TraceChangeManager> _logger = logger; - private readonly List<TracedChanges> undoStack = []; - private readonly List<TracedChanges> redoStack = []; + private readonly List<TracedChanges> _undoStack = []; + private readonly List<TracedChanges> _redoStack = []; - private List<TracedChange>? bulkEditTracedChanges; + private List<TracedChange>? _bulkEditTracedChanges; public event EventHandler? TracedObjectHistoryChanged; public event EventHandler? UndoDone; public event EventHandler? RedoDone; - public bool CurrentlyHandlingRedoOrUndoChanges { get; private set; } = false; - /// <inheritdoc /> - public bool CanUndo - { - get - { - return undoStack.Count > 0; - } - } /// <inheritdoc /> - public bool CanRedo - { - get - { - return redoStack.Count > 0; - } - } + public bool CanUndo => _undoStack.Count > 0; - public void TraceChanges(ITraceable traceable) - { - traceable.TraceablePropertyChanged += Traceable_TraceablePropertyChanged; - } + /// <inheritdoc /> + public bool CanRedo => _redoStack.Count > 0; public void Reset() { - Reset(redoStack); - Reset(undoStack); + _redoStack.Clear(); + _undoStack.Clear(); } public void Undo() @@ -93,12 +48,11 @@ public void Undo() _logger.LogDebug("Undo called"); if (CanUndo) { - CurrentlyHandlingRedoOrUndoChanges = true; TracedChanges? changes = null; - while (undoStack.Count > 0 && changes == null) + while (_undoStack.Count > 0 && changes == null) { - changes = undoStack[^1]; - undoStack.Remove(changes); + changes = _undoStack[^1]; + _undoStack.Remove(changes); if (changes.HasTraceableObject == false) { changes = null; @@ -112,15 +66,21 @@ public void Undo() var change = changes.Changes.ElementAt(i); var tracedObject = change?.TraceableObject; var traceAbleChange = change?.TraceableChange; - _logger.LogDebug("tracedObject = {tracedObject}, traceAbleChange = {traceAbleChange}", tracedObject, traceAbleChange); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("tracedObject = {tracedObject}, traceAbleChange = {traceAbleChange}", tracedObject, traceAbleChange); + } if (tracedObject != null && traceAbleChange != null) { var propertyInfo = tracedObject.GetType().GetProperty(traceAbleChange.PropertyName); if (propertyInfo != null) { var currentValue = propertyInfo.GetValue(tracedObject); - redoChanges.Add(new TracedChange(tracedObject, new TraceableChange(currentValue, traceAbleChange.PropertyName))); - _logger.LogDebug("Setting {PropertyName} on {tracedObject} from {currentValue} to {PreviousValue}", traceAbleChange.PropertyName, tracedObject, currentValue, traceAbleChange.PreviousValue); + redoChanges.Add(new TracedChange(tracedObject, new(currentValue, traceAbleChange.PropertyName))); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("Setting {PropertyName} on {tracedObject} from {currentValue} to {PreviousValue}", traceAbleChange.PropertyName, tracedObject, currentValue, traceAbleChange.PreviousValue); + } propertyInfo.SetValue(tracedObject, traceAbleChange.PreviousValue); } else @@ -134,9 +94,8 @@ public void Undo() } } //Push the old value to redo stack - redoStack.Add(new TracedChanges(redoChanges)); + _redoStack.Add(new TracedChanges(redoChanges)); } - CurrentlyHandlingRedoOrUndoChanges = false; UndoDone?.Invoke(this, EventArgs.Empty); } } @@ -146,12 +105,11 @@ public void Redo() _logger.LogDebug("Redo called"); if (CanRedo) { - CurrentlyHandlingRedoOrUndoChanges = true; TracedChanges? changes = null; - while (redoStack.Count > 0 && changes == null) + while (_redoStack.Count > 0 && changes == null) { - changes = redoStack[^1]; - redoStack.Remove(changes); + changes = _redoStack[^1]; + _redoStack.Remove(changes); if (changes.HasTraceableObject == false) { changes = null; @@ -165,15 +123,21 @@ public void Redo() var change = changes.Changes.ElementAt(i); var tracedObject = change?.TraceableObject; var traceAbleChange = change?.TraceableChange; - _logger.LogDebug("tracedObject = {tracedObject}, traceAbleChange = {traceAbleChange}", tracedObject, traceAbleChange); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("tracedObject = {tracedObject}, traceAbleChange = {traceAbleChange}", tracedObject, traceAbleChange); + } if (tracedObject != null && traceAbleChange != null) { var propertyInfo = tracedObject.GetType().GetProperty(traceAbleChange.PropertyName); if (propertyInfo != null) { var currentValue = propertyInfo.GetValue(tracedObject); - undoChanges.Add(new TracedChange(tracedObject, new TraceableChange(currentValue, traceAbleChange.PropertyName))); - _logger.LogDebug("Setting {PropertyName} on {tracedObject} from {currentValue} to {PreviousValue}", traceAbleChange.PropertyName, tracedObject, currentValue, traceAbleChange.PreviousValue); + undoChanges.Add(new TracedChange(tracedObject, new(currentValue, traceAbleChange.PropertyName))); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("Setting {PropertyName} on {tracedObject} from {currentValue} to {PreviousValue}", traceAbleChange.PropertyName, tracedObject, currentValue, traceAbleChange.PreviousValue); + } propertyInfo.SetValue(tracedObject, traceAbleChange.PreviousValue); } else @@ -187,101 +151,64 @@ public void Redo() } } //Push the old value to redo stack - undoStack.Add(new TracedChanges(undoChanges)); + _undoStack.Add(new TracedChanges(undoChanges)); } - CurrentlyHandlingRedoOrUndoChanges = false; RedoDone?.Invoke(this, EventArgs.Empty); } } public bool BulkEdit { - get => bulkEditTracedChanges != null; + get => _bulkEditTracedChanges != null; set { - _logger.LogDebug("Set BulkEdit called with {value}", value); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("Set BulkEdit called with {value}", value); + } if (value) { - bulkEditTracedChanges = []; + _bulkEditTracedChanges ??= []; } else { - if (bulkEditTracedChanges != null) + if (_bulkEditTracedChanges?.Count > 0) { - undoStack.Add(new TracedChanges(bulkEditTracedChanges)); + _undoStack.Add(new TracedChanges(_bulkEditTracedChanges)); TracedObjectHistoryChanged?.Invoke(this, EventArgs.Empty); - bulkEditTracedChanges = null; } + _bulkEditTracedChanges = null; } } } - public void MergeLastEditWithEdit(Func<TracedChanges, bool> targetEdit) + public void AddChange(TracedChange tracedChange) { - var edit = undoStack.FirstOrDefault(targetEdit); - if ((edit != null) && (undoStack.Count > 0)) + if (_logger.IsEnabled(LogLevel.Debug)) { - var lastEdits = undoStack[^1]; - undoStack.Remove(lastEdits); - edit.Changes.AddRange(lastEdits.Changes); - UndoDone?.Invoke(this, EventArgs.Empty); + _logger.LogDebug("AddChange: {tracedChange.TraceableObject}, {tracedChange.TraceableChange.PropertyName}, {tracedChange.TraceableChange.PreviousValue}", tracedChange.TraceableObject, tracedChange.TraceableChange.PropertyName, tracedChange.TraceableChange.PreviousValue); } - } - - public void RemoveTracedChanges(IEnumerable<ITraceable> traceables) - { - undoStack.RemoveAll(x => x.HasTraceableObject == false); - undoStack.RemoveAll(x => x.Changes.Any(y => traceables.Contains(y.TraceableObject))); - redoStack.RemoveAll(x => x.HasTraceableObject == false); - redoStack.RemoveAll(x => x.Changes.Any(y => traceables.Contains(y.TraceableObject))); - TracedObjectHistoryChanged?.Invoke(this, EventArgs.Empty); - } - - private void Reset(List<TracedChanges> stack) - { - while (stack.Count > 0) + if (BulkEdit == false) { - var tracedChange = stack[^1]; - stack.Remove(tracedChange); - foreach (var change in tracedChange.Changes) - { - if (change.TraceableObject != null) - { - change.TraceableObject.TraceablePropertyChanged -= Traceable_TraceablePropertyChanged; - } - } + //Single change + _undoStack.Add(new([tracedChange])); + _redoStack.Clear(); + TracedObjectHistoryChanged?.Invoke(this, EventArgs.Empty); + } + else + { + //We are tracing multiple changes + _bulkEditTracedChanges?.Add(tracedChange); } - stack.Clear(); } - private void Traceable_TraceablePropertyChanged(object? sender, TraceablePropertiesChangedEventArgs e) + public void RemoveTracedChanges(IEnumerable<object> traceables) { - _logger.LogDebug("Traceable_TraceablePropertyChanged called with {sender}, {PropertyName}, {PreviousValue}", sender, e.TraceableChange.PropertyName, e.TraceableChange.PreviousValue); - _logger.LogDebug("CurrentlyHandlingRedoOrUndoChanges = {CurrentlyHandlingRedoOrUndoChanges}", CurrentlyHandlingRedoOrUndoChanges); - if (CurrentlyHandlingRedoOrUndoChanges == false) - { - if (sender != null) - { - _logger.LogDebug("BulkEdit = {BulkEdit}", BulkEdit); - if (BulkEdit == false) - { - //Single change - var changes = new TracedChanges([new((ITraceable)sender, e.TraceableChange)]); - undoStack.Add(changes); - redoStack.Clear(); - TracedObjectHistoryChanged?.Invoke(this, EventArgs.Empty); - } - else - { - //We are tracing multiple changes - bulkEditTracedChanges?.Add(new TracedChange((ITraceable)sender, e.TraceableChange)); - } - } - else - { - throw new ArgumentNullException(nameof(sender)); - } - } + _undoStack.RemoveAll(x => x.HasTraceableObject == false); + _undoStack.RemoveAll(x => x.Changes.Any(y => traceables.Contains(y.TraceableObject))); + _redoStack.RemoveAll(x => x.HasTraceableObject == false); + _redoStack.RemoveAll(x => x.Changes.Any(y => traceables.Contains(y.TraceableObject))); + TracedObjectHistoryChanged?.Invoke(this, EventArgs.Empty); } } } diff --git a/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor b/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor index 5533938b..122d0742 100644 --- a/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor +++ b/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor @@ -38,7 +38,7 @@ along with Foobar. If not, see { <MudText>@String.Format("--{0}--{1}--", CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator, CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator)</MudText> } - <MudSlider T="double" Variant="Variant.Filled" Min="0" Max="100" Disabled="!_playbackService.PlaybackPossible" Value="sliderValue" ValueChanged="OnSliderValueChanged" ValueLabel aria-label="Playback slider"> + <MudSlider T="double" Variant="Variant.Filled" Min="0" Max="100" Disabled="!_playbackService.IsPlaybackPossible" Value="sliderValue" ValueChanged="OnSliderValueChanged" ValueLabel aria-label="Playback slider"> <ValueLabelContent> @GetSliderTimeValue() </ValueLabelContent> @@ -54,17 +54,17 @@ along with Foobar. If not, see </MudStack> <MudStack Row AlignItems="AlignItems.Center" Justify="Justify.Center"> <MudTooltip Text="@_localizer["Play or pause the current audio"]"> - <MudIconButton Icon="@(_playbackService.IsPlaying ? Icons.Material.Outlined.Pause : Icons.Material.Outlined.PlayArrow)" Variant="Variant.Filled" Color="Color.Primary" Disabled="!_playbackService.PlaybackPossible" OnClick="OnPlayOrPauseClicked" aria-label="Play" /> + <MudIconButton Icon="@(_playbackService.IsPlaying ? Icons.Material.Outlined.Pause : Icons.Material.Outlined.PlayArrow)" Variant="Variant.Filled" Color="Color.Primary" Disabled="!_playbackService.IsPlaybackPossible" OnClick="OnPlayOrPauseClicked" aria-label="Play" /> </MudTooltip> <MudButtonGroup OverrideStyles="false"> <MudTooltip Text="@_localizer["Jump to previous track"]"> - <MudIconButton Icon="@Icons.Material.Outlined.SkipPrevious" Variant="Variant.Filled" Color="Color.Tertiary" Disabled="!_playbackService.PreviousPossible" OnClick="PlayPreviousTrackAsync" aria-label="@_localizer["Jump to previous track"]" /> + <MudIconButton Icon="@Icons.Material.Outlined.SkipPrevious" Variant="Variant.Filled" Color="Color.Tertiary" Disabled="!_playbackService.IsPreviousPossible" OnClick="PlayPreviousTrackAsync" aria-label="@_localizer["Jump to previous track"]" /> </MudTooltip> <MudTooltip Text="@_localizer["Stop the current playback"]"> - <MudIconButton Icon="@Icons.Material.Outlined.Stop" Variant="Variant.Filled" Color="Color.Primary" OnClick="StopAsync" Disabled="!_playbackService.PlaybackPossible" aria-label="@_localizer["Stop the current playback"]" /> + <MudIconButton Icon="@Icons.Material.Outlined.Stop" Variant="Variant.Filled" Color="Color.Primary" OnClick="StopAsync" Disabled="!_playbackService.IsPlaybackPossible" aria-label="@_localizer["Stop the current playback"]" /> </MudTooltip> <MudTooltip Text="@_localizer["Jump to next track"]"> - <MudIconButton Icon="@Icons.Material.Outlined.SkipNext" Variant="Variant.Filled" Color="Color.Tertiary" Disabled="!_playbackService.NextPossible" OnClick="PlayNextTrackAsync" aria-label="@_localizer["Jump to next track"]" /> + <MudIconButton Icon="@Icons.Material.Outlined.SkipNext" Variant="Variant.Filled" Color="Color.Tertiary" Disabled="!_playbackService.IsNextPossible" OnClick="PlayNextTrackAsync" aria-label="@_localizer["Jump to next track"]" /> </MudTooltip> </MudButtonGroup> </MudStack> diff --git a/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor b/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor index cc0cf4eb..6de35334 100644 --- a/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor +++ b/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor @@ -20,26 +20,27 @@ along with Foobar. If not, see @inject IStringLocalizer<CuesheetData> _localizer @inject ValidationService _validationService @inject IFileInputManager _fileInputManager -@inject IBlazorDownloadFileService _blazorDownloadFileService @inject IDialogService _dialogService @inject ISessionStateContainer _sessionStateContainer +@inject ICuesheetManager _cuesheetManager +@inject ITrackManager _trackManager @if (Cuesheet != null) { - <MudForm @ref="form" Model="Cuesheet"> - <MudTextField @bind-Value="Cuesheet.Artist" For="(() => Cuesheet.Artist)" Validation="_validationService.ValidateProperty" Label="@_localizer["Cuesheet artist"]" + <MudForm @ref="_form" Model="Cuesheet"> + <MudTextField Text="@Cuesheet.Artist" TextChanged="(newValue) => _cuesheetManager.SetProperty(x => x.Artist, newValue)" For="(() => Cuesheet.Artist)" Validation="_validationService.ValidateProperty" Label="@_localizer["Cuesheet artist"]" Placeholder="@_localizer["Enter the cuesheet artist here"]" Variant="Variant.Outlined" /> - <MudTextField @bind-Value="Cuesheet.Title" For="(() => Cuesheet.Title)" Validation="_validationService.ValidateProperty" Label="@_localizer["Cuesheet title"]" + <MudTextField Text="@Cuesheet.Title" TextChanged="(newValue) => _cuesheetManager.SetProperty(x => x.Title, newValue)" For="(() => Cuesheet.Title)" Validation="_validationService.ValidateProperty" Label="@_localizer["Cuesheet title"]" Placeholder="@_localizer["Enter the cuesheet title here"]" Variant="Variant.Outlined" /> @switch(CurrentViewMode) { case ViewMode.ImportView: case ViewMode.DetailView: - <FileInput Id="@fileInputAudiofileId" Label="@_localizer["Audiofile"]" FileName="@Cuesheet.Audiofile?.Name" OnFileSelected="OnAudiofileSelected" Error="@fileInputAudiofileErrorText" + <FileInput Id="@_fileInputAudiofileId" Label="@_localizer["Audiofile"]" FileName="@Cuesheet.Audiofile?.Name" OnFileSelected="OnAudiofileSelected" Error="@_fileInputAudiofileErrorText" Filter="@String.Join(",", Audiofile.AudioCodecs.Select(x => x.MimeType))" OnFileRenameClicked="AudioFileRename" FileRenameDisabled="Cuesheet.Audiofile == null" /> <FileInput Label="@_localizer["CD Textfile"]" FileName="@Cuesheet.CDTextfile?.Name" OnFileSelected="OnCDTextfileSelected" Filter="@FileExtensions.CDTextfile" - Error="@fileInputCDTextfileErrorText" OnFileRenameClicked="CDTextFileRename" FileRenameDisabled="Cuesheet.CDTextfile == null" /> - <MudTextField @ref="catalogueNumberTextField" @bind-Value="catalogueNumber" For="(() => Cuesheet.Cataloguenumber)" Validation="_validationService.ValidateProperty" + Error="@_fileInputCDTextfileErrorText" OnFileRenameClicked="CDTextFileRename" FileRenameDisabled="Cuesheet.CDTextfile == null" /> + <MudTextField @ref="_catalogueNumberTextField" @bind-Value="_catalogueNumber" For="(() => Cuesheet.Cataloguenumber)" Validation="_validationService.ValidateProperty" Label="@_localizer["Cataloguenumber"]" Placeholder="@_localizer["Enter the cuesheet catalogue number here"]" Variant="Variant.Outlined" Mask="@(new PatternMask("00 00000 00000 0"))" OnBlur="CataloguenumberOnBlur" /> break; @@ -63,43 +64,43 @@ along with Foobar. If not, see [CascadingParameter] public ViewMode CurrentViewMode { get; set; } - string? fileInputAudiofileId; - string? fileInputAudiofileErrorText; - string? fileInputCDTextfileErrorText; - string? catalogueNumber; - MudForm? form; - MudTextField<string>? catalogueNumberTextField; + string? _fileInputAudiofileId; + string? _fileInputAudiofileErrorText; + string? _fileInputCDTextfileErrorText; + string? _catalogueNumber; + MudForm? _form; + MudTextField<string>? _catalogueNumberTextField; protected override void OnInitialized() { base.OnInitialized(); - if (fileInputAudiofileId == null) + if (_fileInputAudiofileId == null) { - fileInputAudiofileId = $"Input_Audiofile_{Guid.NewGuid()}"; + _fileInputAudiofileId = $"Input_Audiofile_{Guid.NewGuid()}"; } - _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; - _sessionStateContainer.CuesheetChanged += SessionStateContainer_CuesheetChanged; TraceChangeManager.UndoDone += TraceChangeManager_UndoDone; TraceChangeManager.RedoDone += TraceChangeManager_RedoDone; + _sessionStateContainer.CuesheetChanged += SessionStateContainer_CuesheetChanged; + _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; } protected override void Dispose(bool disposing) { base.Dispose(disposing); - _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; - _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; TraceChangeManager.UndoDone -= TraceChangeManager_UndoDone; TraceChangeManager.RedoDone -= TraceChangeManager_RedoDone; + _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; + _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; } protected override void OnParametersSet() { base.OnParametersSet(); - if (form?.IsTouched == true) + if (_form?.IsTouched == true) { SetAudiofileValidationText(); } - catalogueNumber = Cuesheet?.Cataloguenumber; + _catalogueNumber = Cuesheet?.Cataloguenumber; } async Task OnAudiofileSelected(IBrowserFile? browserFile) @@ -108,29 +109,23 @@ along with Foobar. If not, see { return; } - fileInputAudiofileErrorText = null; + _fileInputAudiofileErrorText = null; try { + Audiofile? audiofile = null; if (browserFile != null) { - var fileUpload = await _fileInputManager.CreateFileUploadsAsync([browserFile], fileInputAudiofileId); - Cuesheet.Audiofile = await _fileInputManager.CreateAudiofileAsync(fileUpload.Single()); - if (Cuesheet.RecalculateLastTrackEnd()) - { - TraceChangeManager.MergeLastEditWithEdit(x => x.Changes.All(y => y.TraceableObject == Cuesheet && y.TraceableChange.PropertyName == nameof(Audiofile))); - } - } - else - { - Cuesheet.Audiofile = null; + var fileUpload = await _fileInputManager.CreateFileUploadsAsync([browserFile], _fileInputAudiofileId); + audiofile = await _fileInputManager.CreateAudiofileAsync(fileUpload.Single()); } + _cuesheetManager.SetProperty(x => x.Audiofile, audiofile); } catch(ArgumentException ae) { - fileInputAudiofileErrorText = ae.Message; + _fileInputAudiofileErrorText = ae.Message; } // Just validate the cuesheet if there is no error already - if (fileInputAudiofileErrorText == null) + if (_fileInputAudiofileErrorText == null) { SetAudiofileValidationText(); } @@ -145,11 +140,11 @@ along with Foobar. If not, see var validationMessages = _validationService.Validate(Cuesheet, nameof(Cuesheet.Audiofile)); if (validationMessages.Count() > 0) { - fileInputAudiofileErrorText = String.Join(Environment.NewLine, validationMessages); + _fileInputAudiofileErrorText = String.Join(Environment.NewLine, validationMessages); } else { - fileInputAudiofileErrorText = null; + _fileInputAudiofileErrorText = null; } } @@ -159,21 +154,19 @@ along with Foobar. If not, see { return; } - fileInputCDTextfileErrorText = null; + _fileInputCDTextfileErrorText = null; try { + CDTextfile? newValue = null; if (browserFile != null) { - Cuesheet.CDTextfile = _fileInputManager.CreateCDTextfile(browserFile.ContentType, browserFile.Name); - } - else - { - Cuesheet.CDTextfile = null; + newValue = _fileInputManager.CreateCDTextfile(browserFile.ContentType, browserFile.Name); } + _cuesheetManager.SetProperty(x => x.CDTextfile, newValue); } catch (ArgumentException ae) { - fileInputCDTextfileErrorText = ae.Message; + _fileInputCDTextfileErrorText = ae.Message; } } @@ -225,33 +218,31 @@ along with Foobar. If not, see } } - void SessionStateContainer_ImportCuesheetChanged(object? sender, EventArgs args) + void CataloguenumberOnBlur() { - StateHasChanged(); + var newValue = _catalogueNumber?.Replace(" ", string.Empty); + _cuesheetManager.SetProperty(x => x.Cataloguenumber, newValue); } - void SessionStateContainer_CuesheetChanged(object? sender, EventArgs args) + void TraceChangeManager_RedoDone(object? sender, EventArgs e) { - StateHasChanged(); + _catalogueNumber = Cuesheet?.Cataloguenumber; + _catalogueNumberTextField?.ResetAsync(); } - void CataloguenumberOnBlur() + void TraceChangeManager_UndoDone(object? sender, EventArgs e) { - if (Cuesheet != null) - { - Cuesheet.Cataloguenumber = catalogueNumber?.Replace(" ", string.Empty); - } + _catalogueNumber = Cuesheet?.Cataloguenumber; + _catalogueNumberTextField?.ResetAsync(); } - void TraceChangeManager_RedoDone(object? sender, EventArgs e) + void SessionStateContainer_CuesheetChanged(object? sender, EventArgs args) { - catalogueNumber = Cuesheet?.Cataloguenumber; - catalogueNumberTextField?.ResetAsync(); + StateHasChanged(); } - void TraceChangeManager_UndoDone(object? sender, EventArgs e) + void SessionStateContainer_ImportCuesheetChanged(object? sender, EventArgs args) { - catalogueNumber = Cuesheet?.Cataloguenumber; - catalogueNumberTextField?.ResetAsync(); + StateHasChanged(); } } diff --git a/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.de.resx b/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.de.resx index b03a5f25..c15b5e68 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.de.resx +++ b/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.de.resx @@ -121,7 +121,7 @@ <value>Künstler</value> </data> <data name="Begin" xml:space="preserve"> - <value>Start</value> + <value>Beginn</value> </data> <data name="Change" xml:space="preserve"> <value>Ändern</value> diff --git a/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor b/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor index e029d6f8..6995dc3e 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor @@ -40,19 +40,6 @@ along with Foobar. If not, see </MudItem> <MudItem xs="8" sm="9"><MudSwitch @bind-Value="EditedTrack.IsLinkedToPreviousTrack" Label="@_localizer["Link to previous track"]" Color="Color.Primary" Disabled="editModeTrackIsLinkedToPreviousTrack == DynamicEditValue.DoNotChange" /></MudItem> <MudFlexBreak /> - <MudItem xs="4" sm="3"> - <MudSelect @bind-Value="editModeTrackPosition" Variant="Variant.Outlined" Modal="true"> - <MudSelectItem Value="DynamicEditValue.DoNotChange"><MudIcon Icon="@Icons.Material.Filled.DoDisturb" Style="height: 14px;" /></MudSelectItem> - <MudSelectItem Value="DynamicEditValue.EnteredValueEquals"><MudIcon Icon="@Icons.Material.Filled.Check" Style="height: 14px;" /></MudSelectItem> - <MudSelectItem Value="DynamicEditValue.EnteredValueAdd"><MudIcon Icon="@Icons.Material.Filled.Add" Style="height: 14px;" /></MudSelectItem> - <MudSelectItem Value="DynamicEditValue.EnteredValueSubstract"><MudIcon Icon="@Icons.Material.Filled.Remove" Style="height: 14px;" /></MudSelectItem> - </MudSelect> - </MudItem> - <MudItem xs="8" sm="9"> - <MudTextField @bind-Value="EditedTrack.Position" Label="@_localizer["Position"]" Variant="Variant.Outlined" Disabled="editModeTrackPosition == DynamicEditValue.DoNotChange" Error="!String.IsNullOrEmpty(GetValidationErrorMessage(editModeTrackPosition != DynamicEditValue.DoNotChange, EditedTrack, nameof(Track.Position)))" - ErrorText="@GetValidationErrorMessage(editModeTrackPosition != DynamicEditValue.DoNotChange, EditedTrack, nameof(Track.Position))" /> - </MudItem> - <MudFlexBreak /> <MudItem xs="4" sm="3"> <MudSelect @bind-Value="editModeTrackArtist" Variant="Variant.Outlined" Modal="true"> <MudSelectItem Value="DynamicEditValue.DoNotChange"><MudIcon Icon="@Icons.Material.Filled.DoDisturb" Style="height: 14px;" /></MudSelectItem> @@ -212,11 +199,11 @@ along with Foobar. If not, see [EditorRequired] public Track EditedTrack { get; set; } = null!; - DynamicEditValue editModeTrackIsLinkedToPreviousTrack, editModeTrackPosition, editModeTrackArtist, editModeTrackTitle, editModeTrackBegin, editModeTrackEnd, editModeTrackLength, editModeTrackFlags, editModeTrackPregap, editModeTrackPostgap; + DynamicEditValue editModeTrackIsLinkedToPreviousTrack, editModeTrackArtist, editModeTrackTitle, editModeTrackBegin, editModeTrackEnd, editModeTrackLength, editModeTrackFlags, editModeTrackPregap, editModeTrackPostgap; void SaveClick() { - var result = new EditMultipleTracksModalResult(EditedTrack, editModeTrackIsLinkedToPreviousTrack, editModeTrackPosition, editModeTrackArtist, editModeTrackTitle, editModeTrackBegin, editModeTrackEnd, editModeTrackLength, + var result = new EditMultipleTracksModalResult(EditedTrack, editModeTrackIsLinkedToPreviousTrack, editModeTrackArtist, editModeTrackTitle, editModeTrackBegin, editModeTrackEnd, editModeTrackLength, editModeTrackFlags, editModeTrackPregap, editModeTrackPostgap); MudDialog?.Close(DialogResult.Ok(result)); } diff --git a/AudioCuesheetEditor/Shared/Dialogs/EditTrackModal.razor b/AudioCuesheetEditor/Shared/Dialogs/EditTrackModal.razor index 721e7fe9..e26e5b7c 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/EditTrackModal.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/EditTrackModal.razor @@ -31,7 +31,7 @@ along with Foobar. If not, see <MudSwitch @bind-Value="EditedTrack.IsLinkedToPreviousTrack" Label="@_localizer["Link to previous track"]" Color="Color.Primary" Error="!String.IsNullOrEmpty(GetValidationErrorMessage(EditedTrack, nameof(Track.IsLinkedToPreviousTrack)))" ErrorText="@GetValidationErrorMessage(EditedTrack, nameof(Track.IsLinkedToPreviousTrack))" /> <MudTextField @bind-Value="EditedTrack.Position" Label="@_localizer["Position"]" Variant="Variant.Outlined" Error="!String.IsNullOrEmpty(GetValidationErrorMessage(EditedTrack, nameof(Track.Position)))" - ErrorText="@GetValidationErrorMessage(EditedTrack, nameof(Track.Position))" /> + ErrorText="@GetValidationErrorMessage(EditedTrack, nameof(Track.Position))" Disabled /> @{ MusicBrainzArtist? autocompleteArtist = new() { diff --git a/AudioCuesheetEditor/Shared/Record/AddTrack.razor b/AudioCuesheetEditor/Shared/Record/AddTrack.razor index 102317a2..d461b00a 100644 --- a/AudioCuesheetEditor/Shared/Record/AddTrack.razor +++ b/AudioCuesheetEditor/Shared/Record/AddTrack.razor @@ -21,6 +21,7 @@ along with Foobar. If not, see @inject AutocompleteManager _autocompleteManager @inject ValidationService _validationService @inject ISessionStateContainer _sessionStateContainer +@inject ICuesheetManager _cuesheetManager <MudCard Outlined> <MudCardContent> @@ -28,12 +29,12 @@ along with Foobar. If not, see @{ MusicBrainzArtist? autocompleteArtist = new() { - Name = currentRecordingTrack.Artist + Name = _currentRecordingTrack.Artist }; } - <MudAutocomplete @ref="artistInput" T="MusicBrainzArtist" Label="@_localizer["Artist"]" SearchFunc="_autocompleteManager.SearchArtistsAsync" ToStringFunc="(value) => value?.Name" - @bind-Text="@currentRecordingTrack.Artist" @bind-Value="autocompleteArtist" Clearable ShowProgressIndicator ResetValueOnEmptyText - Error="!String.IsNullOrEmpty(GetValidationErrorMessage(currentRecordingTrack, nameof(Track.Artist)))" ErrorText="@GetValidationErrorMessage(currentRecordingTrack, nameof(Track.Artist))" + <MudAutocomplete @ref="_artistInput" T="MusicBrainzArtist" Label="@_localizer["Artist"]" SearchFunc="_autocompleteManager.SearchArtistsAsync" ToStringFunc="(value) => value?.Name" + @bind-Text="@_currentRecordingTrack.Artist" @bind-Value="autocompleteArtist" Clearable ShowProgressIndicator ResetValueOnEmptyText + Error="!String.IsNullOrEmpty(GetValidationErrorMessage(_currentRecordingTrack, nameof(Track.Artist)))" ErrorText="@GetValidationErrorMessage(_currentRecordingTrack, nameof(Track.Artist))" Disabled="Cuesheet?.IsRecording == false" MaxItems="null" CoerceText="false"> <ItemTemplate Context="autocompleteContext"> @if (autocompleteContext.Disambiguation != null) @@ -49,13 +50,13 @@ along with Foobar. If not, see @{ MusicBrainzTrack? autocompleteTrack = new() { - Artist = currentRecordingTrack.Artist, - Title = currentRecordingTrack.Title + Artist = _currentRecordingTrack.Artist, + Title = _currentRecordingTrack.Title }; } - <MudAutocomplete @ref="titleInput" T="MusicBrainzTrack" Label="@_localizer["Title"]" SearchFunc="(value, token) => _autocompleteManager.SearchTitlesAsync(value, currentRecordingTrack.Artist, token)" ToStringFunc="(value) => value?.Title" - @bind-Text="currentRecordingTrack.Title" @bind-Value="autocompleteTrack" Clearable ShowProgressIndicator ResetValueOnEmptyText - Error="!String.IsNullOrEmpty(GetValidationErrorMessage(currentRecordingTrack, nameof(Track.Title)))" ErrorText="@GetValidationErrorMessage(currentRecordingTrack, nameof(Track.Title))" + <MudAutocomplete @ref="_titleInput" T="MusicBrainzTrack" Label="@_localizer["Title"]" SearchFunc="(value, token) => _autocompleteManager.SearchTitlesAsync(value, _currentRecordingTrack.Artist, token)" ToStringFunc="(value) => value?.Title" + @bind-Text="_currentRecordingTrack.Title" @bind-Value="autocompleteTrack" Clearable ShowProgressIndicator ResetValueOnEmptyText + Error="!String.IsNullOrEmpty(GetValidationErrorMessage(_currentRecordingTrack, nameof(Track.Title)))" ErrorText="@GetValidationErrorMessage(_currentRecordingTrack, nameof(Track.Title))" Disabled="Cuesheet?.IsRecording == false" MaxItems="null" CoerceText="false"> <ItemTemplate Context="autocompleteContext"> @if (autocompleteContext.Disambiguation != null) @@ -82,39 +83,27 @@ along with Foobar. If not, see [CascadingParameter] public ViewMode CurrentViewMode { get; set; } - public Cuesheet? Cuesheet - { - get - { - if (CurrentViewMode == ViewMode.ImportView) - { - return _sessionStateContainer.ImportCuesheet; - } - return _sessionStateContainer.Cuesheet; - } - } + public Cuesheet Cuesheet => _sessionStateContainer.Cuesheet; + + Track _currentRecordingTrack = new(); + MudAutocomplete<MusicBrainzArtist>? _artistInput; + MudAutocomplete<MusicBrainzTrack>? _titleInput; + ApplicationOptions? _applicationOptions; - protected override void OnParametersSet() + protected override void OnInitialized() { - if (Cuesheet != null) - { - Cuesheet.IsRecordingChanged -= Cuesheet_IsRecordingChanged; - } - base.OnParametersSet(); - if (Cuesheet != null) - { - Cuesheet.IsRecordingChanged += Cuesheet_IsRecordingChanged; - } + base.OnInitialized(); + _cuesheetManager.IsRecordingChanged += CuesheetManager_IsRecordingChanged; } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - applicationOptions = await base.LocalStorageOptionsProvider.GetOptionsAsync<ApplicationOptions>(); + _applicationOptions = await base.LocalStorageOptionsProvider.GetOptionsAsync<ApplicationOptions>(); base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; - currentRecordingTrack = new() + _currentRecordingTrack = new() { - IsLinkedToPreviousTrack = applicationOptions!.DefaultIsLinkedToPreviousTrack + IsLinkedToPreviousTrack = _applicationOptions!.DefaultIsLinkedToPreviousTrack }; } @@ -123,16 +112,11 @@ along with Foobar. If not, see base.Dispose(disposing); if (Cuesheet != null) { - Cuesheet.IsRecordingChanged -= Cuesheet_IsRecordingChanged; + _cuesheetManager.IsRecordingChanged -= CuesheetManager_IsRecordingChanged; } base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } - Track currentRecordingTrack = new(); - MudAutocomplete<MusicBrainzArtist>? artistInput; - MudAutocomplete<MusicBrainzTrack>? titleInput; - ApplicationOptions? applicationOptions; - String? GetValidationErrorMessage(object model, string propertyName) { String? validationErrorMessage = null; @@ -146,32 +130,32 @@ along with Foobar. If not, see async Task AddTrackAsync() { - if (Cuesheet?.IsRecording == true) + if (Cuesheet.IsRecording == true) { - Cuesheet?.AddTrack(currentRecordingTrack); - currentRecordingTrack = new() + _cuesheetManager.AddTrack(_currentRecordingTrack); + _currentRecordingTrack = new() { - IsLinkedToPreviousTrack = applicationOptions!.DefaultIsLinkedToPreviousTrack + IsLinkedToPreviousTrack = _applicationOptions!.DefaultIsLinkedToPreviousTrack }; - if (artistInput != null) + if (_artistInput != null) { - await artistInput.FocusAsync(); + await _artistInput.FocusAsync(); } } } - void Cuesheet_IsRecordingChanged(object? sender, EventArgs args) + void CuesheetManager_IsRecordingChanged(object? sender, EventArgs args) { StateHasChanged(); - artistInput?.ClearAsync(); - titleInput?.ClearAsync(); + _artistInput?.ClearAsync(); + _titleInput?.ClearAsync(); } void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions option) { if (option is ApplicationOptions applicationOption) { - applicationOptions = applicationOption; + _applicationOptions = applicationOption; StateHasChanged(); } } diff --git a/AudioCuesheetEditor/Shared/Record/ControlRecording.razor b/AudioCuesheetEditor/Shared/Record/ControlRecording.razor index 0d605db8..5346ce28 100644 --- a/AudioCuesheetEditor/Shared/Record/ControlRecording.razor +++ b/AudioCuesheetEditor/Shared/Record/ControlRecording.razor @@ -21,32 +21,29 @@ along with Foobar. If not, see @inject IDialogService _dialogService @inject IBlazorDownloadFileService _blazorDownloadFileService @inject ISessionStateContainer _sessionStateContainer +@inject ICuesheetManager _cuesheetManager -@if (Cuesheet?.IsRecording == false) +@if (Cuesheet.IsRecording == false) { - var recordingPossibleMessages = Cuesheet?.IsRecordingPossible; - if (recordingPossibleMessages?.Any() == true) + if (String.IsNullOrEmpty(_errorMessage) == false) { <MudAlert Severity="Severity.Warning" Variant="Variant.Filled"> <MudText Typo="Typo.h4">@_localizer["Record not possible"]</MudText> - @foreach (var recordingPossibleMessage in recordingPossibleMessages) - { - <MudText>@_localizer[recordingPossibleMessage]</MudText> - } + <MudText>@_localizer[_errorMessage]</MudText> </MudAlert> } } <MudCard Outlined> <MudCardContent> - @if (Cuesheet?.IsRecording == true) + @if (Cuesheet.IsRecording == true) { <MudAlert Severity="Severity.Info" Variant="Variant.Filled" ContentAlignment="HorizontalAlignment.Center">@_localizer["Recording!"]</MudAlert> <br /> } - @if (startRecordTimer?.Enabled == true) + @if (_startRecordTimer?.Enabled == true) { - <MudAlert Severity="Severity.Warning" Variant="Variant.Filled" ContentAlignment="HorizontalAlignment.Center" ShowCloseIcon CloseIcon="@Icons.Material.Outlined.TimerOff" CloseIconClicked="() => startRecordTimer.Dispose()"> - @_localizer["Record will start in {0} seconds!", ((startRecordTimer.Interval / 1000) - (DateTime.Now - recordTimerStarted).Seconds)] + <MudAlert Severity="Severity.Warning" Variant="Variant.Filled" ContentAlignment="HorizontalAlignment.Center" ShowCloseIcon CloseIcon="@Icons.Material.Outlined.TimerOff" CloseIconClicked="() => _startRecordTimer.Dispose()"> + @_localizer["Record will start in {0} seconds!", ((_startRecordTimer.Interval / 1000) - (DateTime.Now - _recordTimerStarted).Seconds)] </MudAlert> <br /> } @@ -62,7 +59,7 @@ along with Foobar. If not, see <MudMenuItem Disabled="StartRecordingDisabled" OnClick="DisplayStartCountdownDialog">@_localizer["Enter countdown timer"]</MudMenuItem> </MudMenu> </MudButtonGroup> - @if (Cuesheet?.RecordingStart.HasValue == true) + @if ((Cuesheet.IsRecording) && (Cuesheet.RecordingStart.HasValue)) { var recordingTime = DateTime.UtcNow - Cuesheet.RecordingStart; recordingTime = recordingTime.Value.Subtract(new TimeSpan(0, 0, 0, 0, recordingTime.Value.Milliseconds)); @@ -73,66 +70,68 @@ along with Foobar. If not, see <MudText Typo="Typo.h5">@String.Format("--{0}--{1}--", CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator, CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator)</MudText> } <MudHidden Breakpoint="Breakpoint.SmAndUp" Invert> - <MudButton Color="Color.Warning" Variant="Variant.Filled" StartIcon="@Icons.Material.Outlined.Stop" Disabled="Cuesheet?.IsRecording == false" OnClick="StopRecording" aria-label="@_localizer["Stop recording"]">@_localizer["Stop recording"]</MudButton> + <MudButton Color="Color.Warning" Variant="Variant.Filled" StartIcon="@Icons.Material.Outlined.Stop" Disabled="Cuesheet.IsRecording == false" OnClick="StopRecording" aria-label="@_localizer["Stop recording"]">@_localizer["Stop recording"]</MudButton> </MudHidden> <MudHidden Breakpoint="Breakpoint.SmAndUp"> - <MudIconButton Color="Color.Warning" Variant="Variant.Filled" Icon="@Icons.Material.Outlined.Stop" Disabled="Cuesheet?.IsRecording == false" OnClick="StopRecording" aria-label="@_localizer["Stop recording"]" /> + <MudIconButton Color="Color.Warning" Variant="Variant.Filled" Icon="@Icons.Material.Outlined.Stop" Disabled="Cuesheet.IsRecording == false" OnClick="StopRecording" aria-label="@_localizer["Stop recording"]" /> </MudHidden> </MudStack> </MudCardContent> </MudCard> @code { - Timer updateGUITimer = new(500); - Timer? startRecordTimer; - DateTime recordTimerStarted; + Timer _updateGUITimer = new(500); + Timer? _startRecordTimer; + DateTime _recordTimerStarted; + string? _errorMessage = null; - public Cuesheet? Cuesheet - { - get - { - if (CurrentViewMode == ViewMode.ImportView) - { - return _sessionStateContainer.ImportCuesheet; - } - return _sessionStateContainer.Cuesheet; - } - } + public Cuesheet Cuesheet => _sessionStateContainer.Cuesheet; [CascadingParameter] public ViewMode CurrentViewMode { get; set; } - public Boolean StartRecordingDisabled => startRecordTimer?.Enabled == true || Cuesheet?.IsRecordingPossible.Any() == true; + public Boolean StartRecordingDisabled => _startRecordTimer?.Enabled == true || _cuesheetManager.IsRecordingPossible.IsSuccess == false; protected override void OnInitialized() { base.OnInitialized(); - updateGUITimer.AutoReset = true; - updateGUITimer.Elapsed += delegate + _updateGUITimer.AutoReset = true; + _updateGUITimer.Elapsed += delegate { StateHasChanged(); Boolean startRecordTimeEnabled = false; - if (startRecordTimer != null) + if (_startRecordTimer != null) { - startRecordTimeEnabled = startRecordTimer.Enabled; + startRecordTimeEnabled = _startRecordTimer.Enabled; } - if ((startRecordTimeEnabled == false) && (Cuesheet?.IsRecording == false)) + if ((startRecordTimeEnabled == false) && (Cuesheet.IsRecording == false)) { - updateGUITimer.Stop(); + _updateGUITimer.Stop(); } }; + var result = _cuesheetManager.IsRecordingPossible; + if (result.IsSuccess == false) + { + _errorMessage = result.Error!.Message; + } } void StartRecording() { - Cuesheet?.StartRecording(); - updateGUITimer.Start(); - Cuesheet!.Audiofile = null; + var result = _cuesheetManager.StartRecording(); + if (result.IsSuccess) + { + _updateGUITimer.Start(); + } + else + { + _errorMessage = result.Error!.Message; + } } void StopRecording() { - Cuesheet?.StopRecording(); + _cuesheetManager.StopRecording(); } async Task DisplayStartCountdownDialog() @@ -143,16 +142,16 @@ along with Foobar. If not, see var recordCountdownTimer = result?.Data as uint?; if ((result?.Canceled == false) && (recordCountdownTimer.HasValue)) { - startRecordTimer = new Timer(recordCountdownTimer.Value * 1000); - startRecordTimer.Elapsed += delegate + _startRecordTimer = new Timer(recordCountdownTimer.Value * 1000); + _startRecordTimer.Elapsed += delegate { StartRecording(); - startRecordTimer.Stop(); - startRecordTimer.Dispose(); + _startRecordTimer.Stop(); + _startRecordTimer.Dispose(); }; - startRecordTimer.Start(); - updateGUITimer.Start(); - recordTimerStarted = DateTime.Now; + _startRecordTimer.Start(); + _updateGUITimer.Start(); + _recordTimerStarted = DateTime.Now; } } } diff --git a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor index 6e949688..d8f4708d 100644 --- a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor +++ b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor @@ -19,6 +19,7 @@ along with Foobar. If not, see @inject AutocompleteManager _autocompleteManager @inject ValidationService _validationService +@inject ITrackManager _trackManager <MudAutocomplete T="MusicBrainzArtist" SearchFunc="_autocompleteManager.SearchArtistsAsync" ToStringFunc="(MusicBrainzArtist? value) => value?.Name" @bind-Text="_artist" Value="_autocompleteArtist" ValueChanged="ValueChanged" Clearable ShowProgressIndicator Validation="(string? newArtist) => _validationService.Validate(Track, nameof(Track.Artist))" @@ -76,7 +77,7 @@ along with Foobar. If not, see try { await Task.Delay(100, token); - Track.Artist = _artist; + _trackManager.SetProperty(Track, x => x.Artist, _artist); } catch (TaskCanceledException) { diff --git a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor index 68b190ff..44842526 100644 --- a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor +++ b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor @@ -19,6 +19,7 @@ along with Foobar. If not, see @inject AutocompleteManager _autocompleteManager @inject ValidationService _validationService +@inject ITrackManager _trackManager <MudAutocomplete T="MusicBrainzTrack" SearchFunc="(value, token) => _autocompleteManager.SearchTitlesAsync(value, Track.Artist, token)" ToStringFunc="(MusicBrainzTrack? value) => value?.Title" @bind-Text="_title" Value="_autocompleteTrack" ValueChanged="TitleSelected" ResetValueOnEmptyText Clearable ShowProgressIndicator @@ -81,18 +82,18 @@ along with Foobar. If not, see { await Task.Delay(100, token); base.TraceChangeManager.BulkEdit = true; - Track.Title = _title; + _trackManager.SetProperty(Track, x => x.Title, _title); switch (CurrentViewMode) { case ViewMode.DetailView: case ViewMode.ImportView: if ((String.IsNullOrEmpty(Track.Artist)) && (String.IsNullOrEmpty(_autocompleteTrack?.Artist) == false)) { - Track.Artist = _autocompleteTrack.Artist; + _trackManager.SetProperty(Track, x => x.Artist, _autocompleteTrack.Artist); } if ((Track.Length.HasValue == false) && (_autocompleteTrack?.Length.HasValue == true)) { - Track.Length = _autocompleteTrack?.Length; + _trackManager.SetProperty(Track, x => x.Length, _autocompleteTrack.Length); } break; } diff --git a/AudioCuesheetEditor/Shared/TrackList/TrackList.razor b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor index 912fc315..ecf47026 100644 --- a/AudioCuesheetEditor/Shared/TrackList/TrackList.razor +++ b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor @@ -26,6 +26,9 @@ along with Foobar. If not, see @inject PlaybackService _playbackService @inject IDialogService _dialogService @inject ISessionStateContainer _sessionStateContainer +@inject ITrackManager _trackManager +@inject ICuesheetManager _cuesheetManager +@inject ITraceChangeManager _traceChangeManager @switch (CurrentViewMode) { @@ -43,34 +46,29 @@ along with Foobar. If not, see </MudAlert> } <div aria-label="@_localizer["Track table controls"]"> - <TrackListControlButtons AddButtonClicked="AddTrackClicked" EditSelectedTracksClicked="EditSelectedTracksClicked" EditSelectedTracksDisabled="selectedTracks.Count == 0" - DeleteSelectedTracksClicked="DeleteSelectedTracksClicked" DeleteSelectedTracksDisabled="selectedTracks.Count == 0" + <TrackListControlButtons AddButtonClicked="AddTrackClicked" EditSelectedTracksClicked="EditSelectedTracksClicked" EditSelectedTracksDisabled="_selectedTracks.Count == 0" + DeleteSelectedTracksClicked="DeleteSelectedTracksClicked" DeleteSelectedTracksDisabled="_selectedTracks.Count == 0" DeleteAllTracksClicked="OnDeleteAllTracksClicked" DeleteAllTracksDisabled="Cuesheet?.Tracks.Any() == false" - MoveTracksUpDisabled="!(Cuesheet?.MoveTracksPossible(selectedTracks, MoveDirection.Up) == true)" MoveTracksUpClicked="() => Cuesheet?.MoveTracks(selectedTracks, MoveDirection.Up)" - MoveTracksDownDisabled="!(Cuesheet?.MoveTracksPossible(selectedTracks, MoveDirection.Down) == true)" MoveTracksDownClicked="() => Cuesheet?.MoveTracks(selectedTracks, MoveDirection.Down)" - CopySelectedTracksDisabled="selectedTracks.Count != 1" CopySelectedTracksClicked="() => CopyTrackClicked()" - FixedHeader="applicationOptions?.FixedTracksTableHeader == true" FixedHeaderClicked="() => base.LocalStorageOptionsProvider.SaveOptionsValueAsync<ApplicationOptions>(x => x.FixedTracksTableHeader, !applicationOptions?.FixedTracksTableHeader)" /> + MoveTracksUpDisabled="!_cuesheetManager.IsMoveTracksUpPossible(_selectedTracks)" MoveTracksUpClicked="() => _cuesheetManager.MoveTracksUp(_selectedTracks)" + MoveTracksDownDisabled="!_cuesheetManager.IsMoveTracksDownPossible(_selectedTracks)" MoveTracksDownClicked="(MoveTracksDownClicked) => _cuesheetManager.MoveTracksDown(_selectedTracks)" + CopySelectedTracksDisabled="_selectedTracks.Count != 1" CopySelectedTracksClicked="() => CopyTrackClicked()" + FixedHeader="_applicationOptions?.FixedTracksTableHeader == true" FixedHeaderClicked="() => base.LocalStorageOptionsProvider.SaveOptionsValueAsync<ApplicationOptions>(x => x.FixedTracksTableHeader, !_applicationOptions?.FixedTracksTableHeader)" /> </div> break; } -<MudForm @ref="form"> - <MudDataGrid T="Track" Items="Cuesheet?.Tracks" ReadOnly="false" Bordered MultiSelection SelectOnRowClick="false" @bind-SelectedItems="selectedTracks" EditTrigger="DataGridEditTrigger.OnRowClick" - EditMode="DataGridEditMode.Cell" ColumnResizeMode="ResizeMode.Column" RowContextMenuClick="OpenMenuContent" Height="@(applicationOptions?.FixedTracksTableHeader == true ? "600px" : null)" - FixedHeader="applicationOptions?.FixedTracksTableHeader == true" Validator="form" Virtualize> +<MudForm @ref="_form"> + <MudDataGrid T="Track" Items="Cuesheet?.Tracks" ReadOnly="false" Bordered MultiSelection SelectOnRowClick="false" @bind-SelectedItems="_selectedTracks" EditTrigger="DataGridEditTrigger.OnRowClick" + EditMode="DataGridEditMode.Cell" ColumnResizeMode="ResizeMode.Column" RowContextMenuClick="OpenMenuContent" Height="@(_applicationOptions?.FixedTracksTableHeader == true ? "600px" : null)" + FixedHeader="_applicationOptions?.FixedTracksTableHeader == true" Validator="_form" Virtualize SortMode="SortMode.None"> <Columns> <SelectColumn Hidden="CurrentViewMode == ViewMode.RecordView" /> <TemplateColumn Title="@_localizer["Controls"]" Hidden="CurrentViewMode != ViewMode.RecordView"> <EditTemplate> - <MudIconButton Icon="@Icons.Material.Outlined.Delete" Variant="Variant.Filled" Color="Color.Error" OnClick="() => Cuesheet?.RemoveTrack(context.Item)" /> + <MudIconButton Icon="@Icons.Material.Outlined.Delete" Variant="Variant.Filled" Color="Color.Error" OnClick="() => _cuesheetManager.RemoveTracks([context.Item])" /> </EditTemplate> </TemplateColumn> - <PropertyColumn Property="x => x.Position" Title="#" Editable="CurrentViewMode != ViewMode.RecordView" HeaderStyle="width: 1px;"> - <EditTemplate> - <MudNumericField Validation="(uint? newPosition) => _validationService.Validate(context.Item, nameof(Track.Position))" Value="context.Item.Position" - ValueChanged="(uint? newPosition) => PositionChanged(context.Item, newPosition)" /> - </EditTemplate> - </PropertyColumn> + <PropertyColumn Property="x => x.Position" Title="#" Editable="false" HeaderStyle="width: 1px;" /> <PropertyColumn Property="x => x.Artist" Title="@_localizer["Artist"]"> <EditTemplate> <ArtistColumn Track="context.Item" /> @@ -113,7 +111,7 @@ along with Foobar. If not, see @if (Cuesheet?.Tracks.FirstOrDefault() != context.Item) { <MudTooltip Text="@GetLinkedTrackTooltip(context.Item)"> - <MudChip Icon="@GetLinkedTrackIcon(context.Item)" Color="Color.Secondary" OnClick="() => context.Item.IsLinkedToPreviousTrack = !context.Item.IsLinkedToPreviousTrack" /> + <MudChip Icon="@GetLinkedTrackIcon(context.Item)" Color="Color.Secondary" OnClick="() => _trackManager.SetProperty(context.Item, x => x.IsLinkedToPreviousTrack, !context.Item.IsLinkedToPreviousTrack)" /> </MudTooltip> } </EditTemplate> @@ -122,20 +120,20 @@ along with Foobar. If not, see </MudDataGrid> </MudForm> -<MudMenu PositionAtCursor @ref="trackContextMenu"> - @if (contextMenuTrack != null) +<MudMenu PositionAtCursor @ref="_trackContextMenu"> + @if (_contextMenuTrack != null) { - <MudMenuItem Icon="@Icons.Material.Outlined.PlayArrow" Disabled="_playbackService.PlaybackPossible == false" OnClick="() => _playbackService.PlayAsync(contextMenuTrack)">@_localizer["Start playback of this track"]</MudMenuItem> - <MudMenuItem Icon="@Icons.Material.Outlined.CopyAll" OnClick="() => CopyTrackClicked(contextMenuTrack)">@_localizer["Duplicate this track"]</MudMenuItem> + <MudMenuItem Icon="@Icons.Material.Outlined.PlayArrow" Disabled="_playbackService.IsPlaybackPossible == false" OnClick="() => _playbackService.PlayAsync(_contextMenuTrack)">@_localizer["Start playback of this track"]</MudMenuItem> + <MudMenuItem Icon="@Icons.Material.Outlined.CopyAll" OnClick="() => CopyTrackClicked(_contextMenuTrack)">@_localizer["Duplicate this track"]</MudMenuItem> } </MudMenu> @code { - HashSet<Track> selectedTracks = new(); - MudMenu? trackContextMenu; - Track? contextMenuTrack; - ApplicationOptions? applicationOptions; - MudForm? form; + HashSet<Track> _selectedTracks = new(); + MudMenu? _trackContextMenu; + Track? _contextMenuTrack; + ApplicationOptions? _applicationOptions; + MudForm? _form; public Cuesheet? Cuesheet { @@ -156,38 +154,37 @@ along with Foobar. If not, see { base.Dispose(disposing); base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; - _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; + _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - applicationOptions = await base.LocalStorageOptionsProvider.GetOptionsAsync<ApplicationOptions>(); + _applicationOptions = await base.LocalStorageOptionsProvider.GetOptionsAsync<ApplicationOptions>(); base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; - _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; _sessionStateContainer.CuesheetChanged += SessionStateContainer_CuesheetChanged; + _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; } void AddTrackClicked() { var newTrack = new Track() { - IsLinkedToPreviousTrack = applicationOptions!.DefaultIsLinkedToPreviousTrack + IsLinkedToPreviousTrack = _applicationOptions!.DefaultIsLinkedToPreviousTrack }; - Cuesheet?.AddTrack(newTrack); - TraceChangeManager.TraceChanges(newTrack); + _cuesheetManager.AddTrack(newTrack); } async Task EditSelectedTracksClicked() { - await _editTrackModalManager.ShowAndHandleModalEditDialogAsync(selectedTracks); + await _editTrackModalManager.ShowAndHandleModalEditDialogAsync(_selectedTracks); } void DeleteSelectedTracksClicked() { - Cuesheet?.RemoveTracks(selectedTracks.ToList()); - selectedTracks.Clear(); + _cuesheetManager.RemoveTracks(_selectedTracks); + _selectedTracks.Clear(); } async Task OnDeleteAllTracksClicked() @@ -200,8 +197,8 @@ along with Foobar. If not, see var result = await dialog.Result; if (result?.Canceled == false) { - Cuesheet?.RemoveTracks(Cuesheet.Tracks); - selectedTracks.Clear(); + _cuesheetManager.RemoveTracks(Cuesheet!.Tracks); + _selectedTracks.Clear(); } } @@ -229,13 +226,15 @@ along with Foobar. If not, see var trackThatWillBeCopied = trackToCopy; if (trackThatWillBeCopied == null) { - trackThatWillBeCopied = selectedTracks.FirstOrDefault(); + trackThatWillBeCopied = _selectedTracks.FirstOrDefault(); } if (trackThatWillBeCopied != null) { - var copy = new Track(trackThatWillBeCopied); - Cuesheet?.AddTrack(copy); - TraceChangeManager.TraceChanges(copy); + var copy = _trackManager.Clone(trackThatWillBeCopied); + copy.Position = (ushort?)(trackThatWillBeCopied.Position + 1); + copy.Begin = trackThatWillBeCopied.End; + copy.End = null; + _cuesheetManager.AddTrack(copy); await EditTrackModalClicked(copy); } } @@ -247,10 +246,10 @@ along with Foobar. If not, see async Task OpenMenuContent(DataGridRowClickEventArgs<Track> args) { - contextMenuTrack = args.Item; - if (trackContextMenu != null) + _contextMenuTrack = args.Item; + if (_trackContextMenu != null) { - await trackContextMenu.OpenMenuAsync(args.MouseEventArgs); + await _trackContextMenu.OpenMenuAsync(args.MouseEventArgs); } } @@ -282,35 +281,39 @@ along with Foobar. If not, see { if (option is ApplicationOptions applicationOption) { - applicationOptions = applicationOption; + _applicationOptions = applicationOption; StateHasChanged(); } } - async Task PositionChanged(Track track, uint? newPosition) + async Task PositionChanged(Track track, ushort? newPosition) { track.Position = newPosition; - if (form != null) + if (_form != null) { - await form.ValidateAsync(); + await _form.ValidateAsync(); } } - async Task TimespanChanged<TProperty>(Track track, System.Linq.Expressions.Expression<Func<Track, TProperty>> expression, String value) + async Task TimespanChanged(Track track, System.Linq.Expressions.Expression<Func<Track, TimeSpan?>> expression, String value) { - await _applicationOptionsTimeSpanParser.TimespanTextChanged<Track, TProperty>(track, expression, value); - if (form != null) + var timeSpan = await _applicationOptionsTimeSpanParser.GetTimespanAsync(value); + _traceChangeManager.BulkEdit = true; + _trackManager.SetProperty<TimeSpan?>(track, expression, timeSpan); + _trackManager.RecalculateLinkedTracksProperties(track); + _traceChangeManager.BulkEdit = false; + if (_form != null) { - await form.ValidateAsync(); + await _form.ValidateAsync(); } } - void SessionStateContainer_ImportCuesheetChanged(object? sender, EventArgs args) + void SessionStateContainer_CuesheetChanged(object? sender, EventArgs args) { StateHasChanged(); } - void SessionStateContainer_CuesheetChanged(object? sender, EventArgs args) + void SessionStateContainer_ImportCuesheetChanged(object? sender, EventArgs args) { StateHasChanged(); } diff --git a/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.de.resx b/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.de.resx index 11980259..03a8dac3 100644 --- a/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.de.resx +++ b/AudioCuesheetEditor/Shared/ViewModes/ViewModeImport.de.resx @@ -118,7 +118,7 @@ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="Analyze" xml:space="preserve"> - <value>Analysiere</value> + <value>Analysieren</value> </data> <data name="Back" xml:space="preserve"> <value>Zurück</value> diff --git a/AudioCuesheetEditor/_Imports.razor b/AudioCuesheetEditor/_Imports.razor index 911e475a..330acc5e 100644 --- a/AudioCuesheetEditor/_Imports.razor +++ b/AudioCuesheetEditor/_Imports.razor @@ -38,6 +38,7 @@ @using AudioCuesheetEditor.Services.UI @using AudioCuesheetEditor.Services.Validation @using AudioCuesheetEditor.Services.Audio +@using AudioCuesheetEditor.Services.AudioCuesheet @using Microsoft.Extensions.Logging @using Microsoft.Extensions.Localization @using BlazorDownloadFile