Skip to content

Commit 8bfbb36

Browse files
Merge pull request #610 from NeoCoderMatrix86/589-add-drag-n-drop-support-for-audio-files
Add drag n drop support for audio files
2 parents d3ce652 + f6de428 commit 8bfbb36

30 files changed

Lines changed: 991 additions & 106 deletions

‎AudioCuesheetEditor.Tests/Services/AudioCuesheet/AudiofileManagerTests.cs‎

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,13 @@ public async Task SetPropertiesAsync_WithBrowserFile_ShouldSetPropertiesAsync()
116116
// Arrange
117117
var objectUrl = "blob:123";
118118
var filename = "song.mp3";
119-
var inputId = "inputId";
120119
var browserFile = new Mock<IBrowserFile>();
121120
browserFile.SetupGet(b => b.ContentType).Returns("audio/mpeg");
122121
browserFile.SetupGet(b => b.Name).Returns(filename);
123122

124123
var codec = Audiofile.AudioCodecs.First(x => x.FileExtension == ".mp3");
125124
_fileInputManager.Setup(f => f.GetAudioCodec(filename, "audio/mpeg")).Returns(codec);
126-
_fileInputManager.Setup(f => f.GetObjectUrlAsync(inputId)).ReturnsAsync(objectUrl);
125+
_fileInputManager.Setup(f => f.GetObjectUrlAsync(browserFile.Object)).ReturnsAsync(objectUrl);
127126
_jsRuntime.Setup(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.IsAny<object?[]>())).Returns(new ValueTask<double>(90.0));
128127

129128
var audiofile = new Audiofile();
@@ -137,7 +136,7 @@ public async Task SetPropertiesAsync_WithBrowserFile_ShouldSetPropertiesAsync()
137136
}
138137
};
139138
// Act
140-
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object, inputId);
139+
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object);
141140

142141
// Assert
143142
Assert.AreEqual(codec, audiofile.AudioCodec);
@@ -146,7 +145,7 @@ public async Task SetPropertiesAsync_WithBrowserFile_ShouldSetPropertiesAsync()
146145
Assert.AreEqual(TimeSpan.FromSeconds(90), audiofile.Duration);
147146
Assert.AreEqual(1, audiofileChangedFired);
148147
_fileInputManager.Verify(f => f.GetAudioCodec(filename, "audio/mpeg"), Times.Once);
149-
_fileInputManager.Verify(f => f.GetObjectUrlAsync(inputId), Times.Once);
148+
_fileInputManager.Verify(f => f.GetObjectUrlAsync(browserFile.Object), Times.Once);
150149
_jsRuntime.Verify(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.Is<object?[]>(o => o[0] as string == audiofile.ObjectURL)), Times.Once);
151150
}
152151

@@ -156,14 +155,13 @@ public async Task SetProperty_AudiofileWithDuration_SetsLastTrackEndAlsoAsync()
156155
// Arrange
157156
var objectUrl = "blob:123";
158157
var filename = "song.mp3";
159-
var inputId = "inputId";
160158
var browserFile = new Mock<IBrowserFile>();
161159
browserFile.SetupGet(b => b.ContentType).Returns("audio/mpeg");
162160
browserFile.SetupGet(b => b.Name).Returns(filename);
163161

164162
var codec = Audiofile.AudioCodecs.First(x => x.FileExtension == ".mp3");
165163
_fileInputManager.Setup(f => f.GetAudioCodec(filename, "audio/mpeg")).Returns(codec);
166-
_fileInputManager.Setup(f => f.GetObjectUrlAsync(inputId)).ReturnsAsync(objectUrl);
164+
_fileInputManager.Setup(f => f.GetObjectUrlAsync(browserFile.Object)).ReturnsAsync(objectUrl);
167165
_jsRuntime.Setup(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.IsAny<object?[]>())).Returns(new ValueTask<double>(90.0));
168166
var track1 = new Track()
169167
{
@@ -189,7 +187,7 @@ public async Task SetProperty_AudiofileWithDuration_SetsLastTrackEndAlsoAsync()
189187
}
190188
};
191189
// Act
192-
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object, inputId);
190+
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object);
193191

194192
// Assert
195193
Assert.AreEqual(codec, audiofile.AudioCodec);
@@ -199,7 +197,7 @@ public async Task SetProperty_AudiofileWithDuration_SetsLastTrackEndAlsoAsync()
199197
Assert.AreEqual(TimeSpan.FromSeconds(90), track2.End);
200198
Assert.AreEqual(1, audiofileChangedFired);
201199
_fileInputManager.Verify(f => f.GetAudioCodec(filename, "audio/mpeg"), Times.Once);
202-
_fileInputManager.Verify(f => f.GetObjectUrlAsync(inputId), Times.Once);
200+
_fileInputManager.Verify(f => f.GetObjectUrlAsync(browserFile.Object), Times.Once);
203201
_jsRuntime.Verify(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.Is<object?[]>(o => o[0] as string == audiofile.ObjectURL)), Times.Once);
204202
}
205203

@@ -209,14 +207,13 @@ public async Task SetPropertiesAsync_PreviouslySetAudiofile_ShouldClearFirstAndS
209207
// Arrange
210208
var objectUrl = "blob:123";
211209
var filename = "song.mp3";
212-
var inputId = "inputId";
213210
var browserFile = new Mock<IBrowserFile>();
214211
browserFile.SetupGet(b => b.ContentType).Returns("audio/mpeg");
215212
browserFile.SetupGet(b => b.Name).Returns(filename);
216213

217214
var codec = Audiofile.AudioCodecs.First(x => x.FileExtension == ".mp3");
218215
_fileInputManager.Setup(f => f.GetAudioCodec(filename, "audio/mpeg")).Returns(codec);
219-
_fileInputManager.Setup(f => f.GetObjectUrlAsync(inputId)).ReturnsAsync(objectUrl);
216+
_fileInputManager.Setup(f => f.GetObjectUrlAsync(browserFile.Object)).ReturnsAsync(objectUrl);
220217
_jsRuntime.Setup(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.IsAny<object?[]>())).Returns(new ValueTask<double>(90.0));
221218

222219
var audiofile = new Audiofile
@@ -232,7 +229,7 @@ public async Task SetPropertiesAsync_PreviouslySetAudiofile_ShouldClearFirstAndS
232229
}
233230
};
234231
// Act
235-
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object, inputId);
232+
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object);
236233

237234
// Assert
238235
Assert.AreEqual(codec, audiofile.AudioCodec);
@@ -241,7 +238,7 @@ public async Task SetPropertiesAsync_PreviouslySetAudiofile_ShouldClearFirstAndS
241238
Assert.AreEqual(TimeSpan.FromSeconds(90), audiofile.Duration);
242239
Assert.AreEqual(1, audiofileChangedFired);
243240
_fileInputManager.Verify(f => f.GetAudioCodec(filename, "audio/mpeg"), Times.Once);
244-
_fileInputManager.Verify(f => f.GetObjectUrlAsync(inputId), Times.Once);
241+
_fileInputManager.Verify(f => f.GetObjectUrlAsync(browserFile.Object), Times.Once);
245242
_jsRuntime.Verify(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.Is<object?[]>(o => o[0] as string == audiofile.ObjectURL)), Times.Once);
246243
_traceChangeManager.VerifySet(t => t.BulkEdit = true, Times.Once);
247244
_traceChangeManager.VerifySet(t => t.BulkEdit = false, Times.Once);

‎AudioCuesheetEditor.Tests/Services/AudioCuesheet/CuesheetManagerTests.cs‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public void SetProperty_NewValue_ChangesPropertyAndSetsTracedChange()
6464
{
6565
// Arrange
6666
var cuesheet = new Cuesheet();
67+
_sessionStateContainer.SetupGet(x => x.Cuesheet).Returns(cuesheet);
6768
_sessionStateContainer.Setup(x => x.ActiveCuesheet).Returns(cuesheet);
6869
// Act
6970
_cuesheetManager.SetProperty(x => x.Artist, "Artist");
@@ -116,6 +117,21 @@ public void SetProperty_DeletedAudiofile_RevokesObjectURLs()
116117
_jsRuntime.Verify(x => x.InvokeAsync<Microsoft.JSInterop.Infrastructure.IJSVoidResult>("revokeAudioObjectURL", It.Is<object[]>(args => args.Length == 1 && args[0].Equals(audiofile2.ObjectURL))), Times.Once());
117118
}
118119

120+
[TestMethod]
121+
public void SetProperty_OnImportCuesheet_DoesNotTraceChanges()
122+
{
123+
// Arrange
124+
var importCuesheet = new Cuesheet();
125+
var cuesheet = new Cuesheet();
126+
_sessionStateContainer.SetupGet(x => x.ImportCuesheet).Returns(importCuesheet);
127+
_sessionStateContainer.SetupGet(x => x.Cuesheet).Returns(cuesheet);
128+
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(importCuesheet);
129+
// Act
130+
_cuesheetManager.SetProperty(x => x.Artist, nameof(SetProperty_OnImportCuesheet_DoesNotTraceChanges));
131+
// Assert
132+
_traceChangeManager.Verify(x => x.AddChange(It.Is<TracedChange>(y => y.TraceableObject == importCuesheet)), Times.Never);
133+
}
134+
119135
[TestMethod]
120136
public void IsRecordingPossible_WithoutTracks_ReturnsSuccess()
121137
{
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//This file is part of AudioCuesheetEditor.
2+
3+
//AudioCuesheetEditor is free software: you can redistribute it and/or modify
4+
//it under the terms of the GNU General Public License as published by
5+
//the Free Software Foundation, either version 3 of the License, or
6+
//(at your option) any later version.
7+
8+
//AudioCuesheetEditor is distributed in the hope that it will be useful,
9+
//but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
//GNU General Public License for more details.
12+
13+
//You should have received a copy of the GNU General Public License
14+
//along with Foobar. If not, see
15+
//<http: //www.gnu.org/licenses />.
16+
using AudioCuesheetEditor.Model.AudioCuesheet;
17+
using AudioCuesheetEditor.Model.IO.Audio;
18+
using AudioCuesheetEditor.Services.AudioCuesheet;
19+
using AudioCuesheetEditor.Services.IO;
20+
using AudioCuesheetEditor.Services.UI;
21+
using Microsoft.VisualStudio.TestTools.UnitTesting;
22+
using Moq;
23+
using System.Collections.Generic;
24+
using System.Threading.Tasks;
25+
26+
namespace AudioCuesheetEditor.Tests.Services.IO
27+
{
28+
[TestClass]
29+
public class AudioImportServiceTests
30+
{
31+
private readonly AudioImportService _service;
32+
private readonly Mock<IDialogManager> _dialogManager;
33+
private readonly Mock<ISessionStateContainer> _sessionStateContainer;
34+
private readonly Mock<ICuesheetManager> _cuesheetManager;
35+
36+
public AudioImportServiceTests()
37+
{
38+
_dialogManager = new();
39+
_sessionStateContainer = new();
40+
_cuesheetManager = new();
41+
_service = new(_dialogManager.Object, _sessionStateContainer.Object, _cuesheetManager.Object);
42+
}
43+
44+
[TestMethod]
45+
public async Task MapAudioImportAsync_WhenMappingIsNotRequired_ReturnsTrueAsync()
46+
{
47+
// Arrange
48+
var cuesheet = new Cuesheet();
49+
var importAudiofileMapping = new Dictionary<Audiofile, Audiofile?>();
50+
_dialogManager.Setup(x => x.ShowImportAudiofilesDialogAsync()).ReturnsAsync(true);
51+
_sessionStateContainer.SetupGet(x => x.ImportAudiofileMapping).Returns(importAudiofileMapping);
52+
_sessionStateContainer.SetupSet(x => x.ImportAudiofileMapping = It.IsAny<Dictionary<Audiofile, Audiofile?>>()).Callback<Dictionary<Audiofile, Audiofile?>>(cs => importAudiofileMapping = cs);
53+
_sessionStateContainer.SetupGet(x => x.ImportAudiofiles).Returns([]);
54+
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(cuesheet);
55+
// Act
56+
var result = await _service.MapAudioImportAsync();
57+
// Assert
58+
Assert.IsTrue(result);
59+
}
60+
61+
[TestMethod]
62+
public async Task MapAudioImportAsync_WhenMappingIsRequiredAndDoneAutomatically_ReturnsTrueAsync()
63+
{
64+
// Arrange
65+
var cuesheet = new Cuesheet()
66+
{
67+
Audiofiles = [
68+
new()
69+
]
70+
};
71+
var importAudiofileMapping = new Dictionary<Audiofile, Audiofile?>
72+
{
73+
{ new(), null }
74+
};
75+
var importAudioFiles = new List<Audiofile>
76+
{
77+
new()
78+
};
79+
_dialogManager.Setup(x => x.ShowImportAudiofilesDialogAsync()).ReturnsAsync(true);
80+
_sessionStateContainer.SetupGet(x => x.ImportAudiofileMapping).Returns(importAudiofileMapping);
81+
_sessionStateContainer.SetupSet(x => x.ImportAudiofileMapping = It.IsAny<Dictionary<Audiofile, Audiofile?>>()).Callback<Dictionary<Audiofile, Audiofile?>>(cs => importAudiofileMapping = cs);
82+
_sessionStateContainer.SetupGet(x => x.ImportAudiofiles).Returns(importAudioFiles);
83+
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(cuesheet);
84+
// Act
85+
var result = await _service.MapAudioImportAsync();
86+
// Assert
87+
Assert.IsTrue(result);
88+
}
89+
90+
[TestMethod]
91+
public async Task MapAudioImportAsync_WhenMappingIsRequiredAndCannotBeDoneAutomatically_ReturnsDialogResultAsync()
92+
{
93+
// Arrange
94+
var cuesheet = new Cuesheet()
95+
{
96+
Audiofiles = [
97+
new()
98+
]
99+
};
100+
var importAudiofileMapping = new Dictionary<Audiofile, Audiofile?>
101+
{
102+
{ new(), null }
103+
};
104+
var importAudioFiles = new List<Audiofile>
105+
{
106+
new(), new()
107+
};
108+
var dialogResultAborted = true;
109+
_dialogManager.Setup(x => x.ShowImportAudiofilesDialogAsync()).ReturnsAsync(dialogResultAborted);
110+
_sessionStateContainer.SetupGet(x => x.ImportAudiofileMapping).Returns(importAudiofileMapping);
111+
_sessionStateContainer.SetupSet(x => x.ImportAudiofileMapping = It.IsAny<Dictionary<Audiofile, Audiofile?>>()).Callback<Dictionary<Audiofile, Audiofile?>>(cs => importAudiofileMapping = cs);
112+
_sessionStateContainer.SetupGet(x => x.ImportAudiofiles).Returns(importAudioFiles);
113+
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(cuesheet);
114+
// Act
115+
var result = await _service.MapAudioImportAsync();
116+
// Assert
117+
Assert.AreEqual(!dialogResultAborted, result);
118+
}
119+
}
120+
}

‎AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,15 +232,14 @@ public async Task CreateFileUploadsAsync_ReturnsFileUploads_WhenFileHasTextConte
232232
// Arrange
233233
var firstFile = CreateBrowserFile("Test.txt", "text/plain", "Just a test!");
234234
var secondFile = CreateBrowserFile("Test.mp3", "audio/mpeg");
235-
var fileInputId = nameof(CreateFileUploadsAsync_ReturnsFileUploads_WhenFileHasTextContentAsync);
236235
IReadOnlyList<IBrowserFile> browserfiles = [
237236
firstFile,
238237
secondFile
239238
];
240239
var objectUrl = "Some object url!";
241240
_jsRuntimeMock.Setup(js => js.InvokeAsync<String>(It.IsAny<string>(), It.IsAny<object[]>())).ReturnsAsync(objectUrl);
242241
// Act
243-
var result = await _service.CreateFileUploadsAsync(browserfiles, fileInputId);
242+
var result = await _service.CreateFileUploadsAsync(browserfiles);
244243
// Assert
245244
Assert.HasCount(2, result);
246245
Assert.AreEqual(firstFile.Name, result.First().Name);
@@ -265,7 +264,7 @@ public async Task CreateFileUploadsAsync_ReturnsEmpty_WhenFilesHaveInvalidMimeTy
265264
// Act
266265
var result = await _service.CreateFileUploadsAsync(browserfiles);
267266
// Assert
268-
Assert.HasCount(0, result);
267+
Assert.IsEmpty(result);
269268
}
270269

271270
static IBrowserFile CreateBrowserFile(string name, string contentType, string? content = null)

‎AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class ImportManagerTests
4343
private readonly Mock<IFileInputManager> _fileInputManagerMock;
4444
private readonly Mock<ITextImportService> _textImportServiceMock;
4545
private readonly Mock<ITrackManager> _trackManagerMock;
46+
private readonly Mock<IAudioImportService> _audioImportService;
4647
public ImportManagerTests()
4748
{
4849
_traceChangeManagerMock = new();
@@ -105,8 +106,9 @@ public ImportManagerTests()
105106
_trackManagerMock.Object.CopyValues(track, clone, setLength: setLength);
106107
return clone;
107108
});
109+
_audioImportService = new();
108110
var loggerMock = new Mock<ILogger<ImportManager>>();
109-
_service = new ImportManager(_sessionStateContainerMock.Object, _traceChangeManagerMock.Object, _fileInputManagerMock.Object, _textImportServiceMock.Object, _trackManagerMock.Object, loggerMock.Object);
111+
_service = new ImportManager(_sessionStateContainerMock.Object, _traceChangeManagerMock.Object, _fileInputManagerMock.Object, _textImportServiceMock.Object, _trackManagerMock.Object, _audioImportService.Object, loggerMock.Object);
110112
}
111113

112114
void TrackManager_SetValue<TProperty>(Track track, Expression<Func<Track, TProperty>> propertyExpression, TProperty value, Boolean signalTraceChangeManager = true)
@@ -385,7 +387,7 @@ public async Task UploadFilesAsync_WithAudiofile_ImportsCorrectly()
385387
}
386388

387389
[TestMethod]
388-
public void ImportCuesheet_WithImportCuesheetAvailable_ImportsCuesheetData()
390+
public async Task ImportCuesheetAsync_WithImportCuesheetAvailable_ImportsCuesheetDataAsync()
389391
{
390392
// Arrange
391393
var track1 = new Track()
@@ -425,7 +427,7 @@ public void ImportCuesheet_WithImportCuesheetAvailable_ImportsCuesheetData()
425427
IList<Audiofile>? sessionStateContainerImportAudiofiles = [];
426428
_sessionStateContainerMock.SetupGet(x => x.ImportAudiofiles).Returns(() => sessionStateContainerImportAudiofiles);
427429
// Act
428-
_service.ImportCuesheet();
430+
await _service.ImportCuesheetAsync();
429431
// Assert
430432
Assert.IsNotNull(sessionStateContainerCuesheet);
431433
Assert.AreEqual(sessionStateContainerImportCuesheet.Artist, sessionStateContainerCuesheet.Artist);

0 commit comments

Comments
 (0)