Skip to content

Commit 4f32ee0

Browse files
Added tests for setting properties on AudioNodes.
1 parent ee6ae0b commit 4f32ee0

14 files changed

+190
-43
lines changed

src/KristofferStrube.Blazor.WebAudio/AudioNodes/AudioNode.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ public abstract class AudioNode : EventTarget
3232
/// <inheritdoc cref="IJSCreatable{AudioNode}.CreateAsync(IJSRuntime, IJSObjectReference, CreationOptions)"/>
3333
protected AudioNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
3434
{
35-
webAudioHelperTask = new(jSRuntime.GetHelperAsync);
3635
if (jSRuntime is not IJSInProcessRuntime || ErrorHandlingJSInterop.ErrorHandlingJSInteropHasBeenSetup)
3736
{
3837
errorHandlingJSReference = new ErrorHandlingJSObjectReference(jSRuntime, jSReference);
38+
webAudioHelperTask = new(async () => await jSRuntime.GetErrorHandlingHelperAsync());
39+
}
40+
else
41+
{
42+
webAudioHelperTask = new(jSRuntime.GetHelperAsync);
3943
}
4044
}
4145

@@ -264,6 +268,10 @@ public async Task<ChannelCountMode> GetChannelCountModeAsync()
264268
/// The default value is <see cref="ChannelCountMode.Max"/>.
265269
/// This attribute has no effect for nodes with no inputs.
266270
/// </summary>
271+
/// <remarks>
272+
/// Throws an <see cref="InvalidStateErrorException"/> if the audio node type does not support changing the channel count mode.<br />
273+
/// Throws an <see cref="NotSupportedErrorException"/> if the channel count mode is not supported for the audio node type.<br />
274+
/// </remarks>
267275
public async Task SetChannelCountModeAsync(ChannelCountMode value)
268276
{
269277
IJSObjectReference helper = await webAudioHelperTask.Value;
@@ -275,6 +283,9 @@ public async Task SetChannelCountModeAsync(ChannelCountMode value)
275283
/// The default value is <see cref="ChannelInterpretation.Speakers"/>.
276284
/// This attribute has no effect for nodes with no inputs.
277285
/// </summary>
286+
/// <remarks>
287+
/// Throws an <see cref="InvalidStateErrorException"/> if the audio node type does not support changing the channel interpretation.<br />
288+
/// </remarks>
278289
public async Task<ChannelInterpretation> GetChannelInterpretationAsync()
279290
{
280291
IJSObjectReference helper = await webAudioHelperTask.Value;
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1-
namespace KristofferStrube.Blazor.WebAudio.Options;
1+
using Microsoft.JSInterop;
2+
using System.Text.Json.Serialization;
23

4+
namespace KristofferStrube.Blazor.WebAudio.Options;
5+
6+
/// <summary>
7+
/// This specifies the options to use in constructing a <see cref="MediaElementAudioSourceNode"/>.
8+
/// </summary>
9+
/// <remarks>
10+
/// <see href="https://www.w3.org/TR/webaudio/#MediaElementAudioSourceOptions">See the API definition here</see>.
11+
/// </remarks>
312
public class MediaElementAudioSourceOptions
413
{
14+
/// <summary>
15+
/// The media element that will be re-routed.
16+
/// </summary>
17+
[JsonPropertyName("mediaElement")]
18+
public required IJSObjectReference MediaElement { get; set; }
519
}

src/KristofferStrube.Blazor.WebAudio/Options/MediaStreamAudioDestinationOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace KristofferStrube.Blazor.WebAudio.Options;
22

33
/// <summary>
4-
/// This specifies the options to use in constructing an <see cref="MediaStreamAudioDestinationNode"/>.
4+
/// This specifies the options to use in constructing a <see cref="MediaStreamAudioDestinationNode"/>.
55
/// </summary>
66
/// <remarks>
77
/// This type doesn't exist in the specs, but the <see cref="MediaStreamAudioDestinationNode"/> has a non-standard value for the <see cref="AudioNodeOptions.ChannelCountMode"/> attribute which is why this type is added in this wrapper.<br />

src/KristofferStrube.Blazor.WebAudio/Options/MediaStreamAudioSourceOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace KristofferStrube.Blazor.WebAudio;
55

66
/// <summary>
7-
/// This specifies the options to use in constructing an <see cref="MediaStreamAudioSourceNode"/>.
7+
/// This specifies the options to use in constructing a <see cref="MediaStreamAudioSourceNode"/>.
88
/// </summary>
99
/// <remarks><see href="https://www.w3.org/TR/webaudio/#MediaStreamAudioSourceOptions">See the API definition here</see>.</remarks>
1010
public class MediaStreamAudioSourceOptions

src/KristofferStrube.Blazor.WebAudio/Options/MediaStreamTrackAudioSourceOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace KristofferStrube.Blazor.WebAudio;
55

66
/// <summary>
7-
/// This specifies the options to use in constructing an <see cref="MediaStreamTrackAudioSourceNode"/>.
7+
/// This specifies the options to use in constructing a <see cref="MediaStreamTrackAudioSourceNode"/>.
88
/// </summary>
99
/// <remarks><see href="https://www.w3.org/TR/webaudio/#MediaStreamTrackAudioSourceOptions">See the API definition here</see>.</remarks>
1010
public class MediaStreamTrackAudioSourceOptions

tests/BlazorServer/Pages/Index.razor

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
<span data-testid="result">@result</span>
44

5+
<HeadContent>
6+
<script>
7+
function getAudioElement() {
8+
let audioElement = document.createElement("audio");
9+
return audioElement;
10+
}
11+
</script>
12+
</HeadContent>
13+
514
@code {
615
string? result;
716

tests/IntegrationTests/AudioNodeTests/AudioNodeTest.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ public abstract class AudioNodeTest<TAudioNode> : AudioContextBlazorTest where T
77
{
88
public abstract Task<TAudioNode> GetDefaultInstanceAsync();
99

10+
public virtual Dictionary<ChannelCountMode, Type> UnsupportedChannelCountModes => [];
11+
12+
public virtual Dictionary<ChannelInterpretation, Type> UnsupportedChannelInterpretations => [];
13+
1014
[Test]
1115
public async Task CreateAsync_WithNoOptions_Succeeds()
1216
{
@@ -24,4 +28,67 @@ public async Task CreateAsync_WithNoOptions_Succeeds()
2428
_ = EvaluationContext.Result.Should().BeOfType<TAudioNode>();
2529
}
2630

31+
[TestCase(ChannelCountMode.Max)]
32+
[TestCase(ChannelCountMode.Explicit)]
33+
[TestCase(ChannelCountMode.ClampedMax)]
34+
[Test]
35+
public async Task SettingChannelCountMode_SetsChannelCountMode_ExceptForUnsupportedValues(ChannelCountMode mode)
36+
{
37+
// Arrange
38+
AfterRenderAsync = async () =>
39+
{
40+
await using TAudioNode node = await GetDefaultInstanceAsync();
41+
42+
await node.SetChannelCountModeAsync(mode);
43+
44+
return await node.GetChannelCountModeAsync();
45+
};
46+
47+
// Act
48+
await OnAfterRerenderAsync();
49+
50+
// Assert
51+
if (UnsupportedChannelCountModes.TryGetValue(mode, out Type? exceptionType))
52+
{
53+
_ = EvaluationContext.Result.Should().Be(null);
54+
_ = EvaluationContext.Exception.Should().BeOfType(exceptionType);
55+
}
56+
else
57+
{
58+
_ = EvaluationContext.Exception.Should().BeNull();
59+
_ = EvaluationContext.Result.Should().Be(mode);
60+
}
61+
}
62+
63+
[TestCase(ChannelInterpretation.Discrete)]
64+
[TestCase(ChannelInterpretation.Speakers)]
65+
[Test]
66+
public async Task SettingChannelInterpretation_SetsInterpretation_ExceptForUnsupportedValues(ChannelInterpretation interpretation)
67+
{
68+
// Arrange
69+
AfterRenderAsync = async () =>
70+
{
71+
await using TAudioNode node = await GetDefaultInstanceAsync();
72+
73+
await node.SetChannelInterpretationAsync(interpretation);
74+
75+
return await node.GetChannelInterpretationAsync();
76+
};
77+
78+
// Act
79+
await OnAfterRerenderAsync();
80+
81+
// Assert
82+
if (UnsupportedChannelInterpretations.TryGetValue(interpretation, out Type? exceptionType))
83+
{
84+
_ = EvaluationContext.Result.Should().Be(null);
85+
_ = EvaluationContext.Exception.Should().BeOfType(exceptionType);
86+
}
87+
else
88+
{
89+
_ = EvaluationContext.Exception.Should().BeNull();
90+
_ = EvaluationContext.Result.Should().Be(interpretation);
91+
}
92+
}
93+
2794
}

tests/IntegrationTests/AudioNodeTests/AudioNodeWithAudioNodeOptions.cs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ public override async Task<TAudioNode> GetDefaultInstanceAsync()
1212
return await CreateAsync(EvaluationContext.JSRuntime, await EvaluationContext.GetAudioContext(), null);
1313
}
1414

15-
public virtual Dictionary<ChannelCountMode, Type> UnsupportedChannelCountModes => [];
16-
1715
[Test]
1816
public async Task CreateAsync_WithEmptyOptions_Succeeds()
1917
{
@@ -83,7 +81,7 @@ public async Task CreateAsync_WithEmptyOptions_HasSameChannelInterpretationAsWhe
8381
[TestCase(ChannelCountMode.Explicit)]
8482
[TestCase(ChannelCountMode.ClampedMax)]
8583
[Test]
86-
public async Task CreateAsync_WithDifferentChannelCountModes_SetsChannelCount_ExceptForUnsupportedValues(ChannelCountMode mode)
84+
public async Task CreateAsync_WithDifferentChannelCountModes_SetsChannelCountMode_ExceptForUnsupportedValues(ChannelCountMode mode)
8785
{
8886
// Arrange
8987
AfterRenderAsync = async () =>
@@ -111,4 +109,36 @@ public async Task CreateAsync_WithDifferentChannelCountModes_SetsChannelCount_Ex
111109
_ = EvaluationContext.Result.Should().Be(mode);
112110
}
113111
}
112+
113+
[TestCase(ChannelInterpretation.Discrete)]
114+
[TestCase(ChannelInterpretation.Speakers)]
115+
[Test]
116+
public async Task CreateAsync_WithDifferentChannelInterpretations_SetsChannelInterpretation_ExceptForUnsupportedValues(ChannelInterpretation interpretation)
117+
{
118+
// Arrange
119+
AfterRenderAsync = async () =>
120+
{
121+
TAudioNodeOptions options = new();
122+
options.ChannelInterpretation = interpretation;
123+
124+
await using TAudioNode node = await CreateAsync(EvaluationContext.JSRuntime, await EvaluationContext.GetAudioContext(), options);
125+
126+
return await node.GetChannelInterpretationAsync();
127+
};
128+
129+
// Act
130+
await OnAfterRerenderAsync();
131+
132+
// Assert
133+
if (UnsupportedChannelInterpretations.TryGetValue(interpretation, out Type? exceptionType))
134+
{
135+
_ = EvaluationContext.Result.Should().Be(null);
136+
_ = EvaluationContext.Exception.Should().BeOfType(exceptionType);
137+
}
138+
else
139+
{
140+
_ = EvaluationContext.Exception.Should().BeNull();
141+
_ = EvaluationContext.Result.Should().Be(interpretation);
142+
}
143+
}
114144
}

tests/IntegrationTests/AudioNodeTests/ChannelSplitterNodeTest.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@ public override async Task<ChannelSplitterNode> CreateAsync(IJSRuntime jSRuntime
1313
[ChannelCountMode.Max] = typeof(InvalidStateErrorException),
1414
[ChannelCountMode.ClampedMax] = typeof(InvalidStateErrorException),
1515
};
16+
17+
public override Dictionary<ChannelInterpretation, Type> UnsupportedChannelInterpretations => new()
18+
{
19+
[ChannelInterpretation.Speakers] = typeof(InvalidStateErrorException),
20+
};
1621
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using KristofferStrube.Blazor.WebAudio.Options;
2+
using Microsoft.JSInterop;
3+
4+
namespace IntegrationTests.AudioNodeTests;
5+
6+
public class MediaElementAudioSourceNodeTest : AudioNodeTest<MediaElementAudioSourceNode>
7+
{
8+
public override async Task<MediaElementAudioSourceNode> GetDefaultInstanceAsync()
9+
{
10+
IJSObjectReference element = await EvaluationContext.GetAudioElementAyns();
11+
return await MediaElementAudioSourceNode.CreateAsync(EvaluationContext.JSRuntime, await EvaluationContext.GetAudioContext(), new MediaElementAudioSourceOptions()
12+
{
13+
MediaElement = element
14+
});
15+
}
16+
}

0 commit comments

Comments
 (0)