From 71e62f6f6e8fc3703a92075d0cbb7b40ff327842 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Mon, 6 Apr 2020 09:55:14 +0100 Subject: [PATCH 01/20] Added question template and moved release drafter to GitHub Actions --- .github/ISSUE_TEMPLATE/question.md | 10 ++++++++++ .github/release-drafter.yml | 20 ++++++++++---------- .github/workflows/release-drafter.yml | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/question.md create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..e6de743 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,10 @@ +--- +name: Question +about: Ask a question about this project +title: "[Question]" +labels: Question, Triage +assignees: '' + +--- + +**Please be sure to check the readme file before raising an issue.** diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 16c5ce0..5778a3d 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,16 +1,16 @@ -name-template: 'v$NEXT_PATCH_VERSION 🌈' +name-template: 'v$NEXT_PATCH_VERSION' tag-template: 'v$NEXT_PATCH_VERSION' +template: | + # What's Changed + + $CHANGES categories: - title: '🚀 Features' - labels: - - 'Feature' + label: 'Feature' - title: '🐛 Bug Fixes' - labels: - - 'Bug' + label: 'Bug' - title: '🧰 Maintenance' label: 'Maintenance' -change-template: '- (#$NUMBER) $TITLE - Thanks to @$AUTHOR ' -template: | - # What's Changed - - $CHANGES \ No newline at end of file + - title: '📖 Documentation' + label: 'Docs' +change-template: '- (#$NUMBER) $TITLE - Thanks to @$AUTHOR ' \ No newline at end of file diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..3fb8ef3 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,14 @@ +name: Release Drafter + +on: + push: + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 589f7dee065d752e84ef2d647187fb4762f196f3 Mon Sep 17 00:00:00 2001 From: Jan Johansson Date: Thu, 21 May 2020 19:50:43 +0200 Subject: [PATCH 02/20] Added configuration option for JsonSerializer, added TimespanJsonConverter and amended documentation --- README.md | 78 +++++++++++++++---- .../Blazored.SessionStorage.csproj | 7 +- .../ISessionStorageService.cs | 2 +- .../ISyncSessionStorageService.cs | 2 +- .../JsonConverters/TimespanJsonConverter.cs | 46 +++++++++++ .../ServiceCollectionExtensions.cs | 23 +++++- .../SessionStorageService.cs | 49 +++++++----- .../StorageOptions/SessionStorageOptions.cs | 18 +++++ 8 files changed, 180 insertions(+), 45 deletions(-) create mode 100644 src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs create mode 100644 src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs diff --git a/README.md b/README.md index aedfe9d..8a08178 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Blazored SessionStorage -A library to provide access to session storage in Blazor applications +A library to provide access to local storage in Blazor applications [![Build Status](https://dev.azure.com/blazored/SessionStorage/_apis/build/status/Blazored.SessionStorage?branchName=master)](https://dev.azure.com/blazored/SessionStorage/_build/latest?definitionId=1&branchName=master) -![Nuget](https://img.shields.io/nuget/v/blazored.sessionstorage.svg) +[![Nuget](https://img.shields.io/nuget/v/blazored.localstorage.svg)](https://www.nuget.org/packages/Blazored.SessionStorage/) ### Installing @@ -11,11 +11,11 @@ You can install from NuGet using the following command: `Install-Package Blazored.SessionStorage` -Or via the Visual Studio package manger. +Or via the Visual Studio package manager. ### Setup -You will need to register the session storage services with the service collection in your _startup.cs_ file. +You will need to register the local storage services with the service collection in your _Startup.cs_ file in Blazor Server. ```c# public void ConfigureServices(IServiceCollection services) @@ -24,34 +24,75 @@ public void ConfigureServices(IServiceCollection services) } ``` +Or in your _Program.cs_ file in Blazor WebAssembly. + +```c# +public static async Task Main(string[] args) +{ + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("app"); + + builder.Services.AddBlazoredSessionStorage(); + + await builder.Build().RunAsync(); +} +``` + +### Configuration + +The local storage provides options that can be modified by you at registration in your _Startup.cs_ file in Blazor Server. + + +```c# +public void ConfigureServices(IServiceCollection services) +{ + services.AddBlazoredSessionStorage(config => + config.JsonSerializerOptions.WriteIndented = true); +} +``` +Or in your _Program.cs_ file in Blazor WebAssembly. + +```c# +public static async Task Main(string[] args) +{ + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("app"); + + builder.Services.AddBlazoredSessionStorage(config => + config.JsonSerializerOptions.WriteIndented = true); + + await builder.Build().RunAsync(); +} +``` + ### Usage (Blazor WebAssembly) To use Blazored.SessionStorage in Blazor WebAssembly, inject the `ISessionStorageService` per the example below. ```c# -@inject Blazored.SessionStorage.ISessionStorageService sessionStorage +@inject Blazored.SessionStorage.ISessionStorageService localStorage @code { protected override async Task OnInitializedAsync() { - await sessionStorage.SetItemAsync("name", "John Smith"); - var name = await sessionStorage.GetItemAsync("name"); + await localStorage.SetItemAsync("name", "John Smith"); + var name = await localStorage.GetItemAsync("name"); } } ``` -With Blazor WebAssembly you also have the option of a synchronous API, if your use case requires it. You can swap the `ISessionStorageService` for `ISyncSessionStorageService` which allows you to avoid use of `async`/`await`. For either interface, the method names are the same. +With Blazor WebAssembly you also have the option of a synchronous API, if your use case requires it. You can swap the `ISessionStorageService` for `ISyncStorageService` which allows you to avoid use of `async`/`await`. For either interface, the method names are the same. ```c# -@inject Blazored.SessionStorage.ISyncSessionStorageService sessionStorage +@inject Blazored.SessionStorage.ISyncStorageService localStorage @code { protected override void OnInitialized() { - sessionStorage.SetItem("name", "John Smith"); - var name = sessionStorage.GetItem("name"); + localStorage.SetItem("name", "John Smith"); + var name = localStorage.GetItem("name"); } } @@ -62,14 +103,14 @@ With Blazor WebAssembly you also have the option of a synchronous API, if your u **NOTE:** Due to pre-rendering in Blazor Server you can't perform any JS interop until the `OnAfterRender` lifecycle method. ```c# -@inject Blazored.SessionStorage.ISessionStorageService sessionStorage +@inject Blazored.SessionStorage.ISessionStorageService localStorage -@functions { +@code { - protected override async Task OnAfterRenderAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { - await sessionStorage.SetItemAsync("name", "John Smith"); - var name = await sessionStorage.GetItemAsync("name"); + await localStorage.SetItemAsync("name", "John Smith"); + var name = await localStorage.GetItemAsync("name"); } } @@ -84,12 +125,15 @@ The APIs available are: - ClearAsync() - LengthAsync() - KeyAsync() -- synchronous via `ISyncSessionStorageService`: + - ContainsKeyAsync() + +- synchronous via `ISyncStorageService` (Synchronous methods are **only** available in Blazor WebAssembly): - SetItem() - GetItem() - RemoveItem() - Clear() - Length() - Key() + - ContainsKey() **Note:** Blazored.SessionStorage methods will handle the serialisation and de-serialisation of the data for you. diff --git a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj index 35c85c1..c6c6963 100644 --- a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj +++ b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj @@ -4,7 +4,6 @@ netstandard2.0 3.0 Blazored.SessionStorage - 3.2 Blazored.SessionStorage 1.0.11 @@ -18,10 +17,10 @@ Blazor SessionStorage Blazored Razor Components - + - - + + diff --git a/src/Blazored.SessionStorage/ISessionStorageService.cs b/src/Blazored.SessionStorage/ISessionStorageService.cs index f8d85cd..97724f7 100644 --- a/src/Blazored.SessionStorage/ISessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISessionStorageService.cs @@ -15,7 +15,7 @@ public interface ISessionStorageService Task RemoveItemAsync(string key); - Task SetItemAsync(string key, object data); + Task SetItemAsync(string key, T data); event EventHandler Changing; event EventHandler Changed; diff --git a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs index b85ee84..a994f6a 100644 --- a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs @@ -14,7 +14,7 @@ public interface ISyncSessionStorageService void RemoveItem(string key); - void SetItem(string key, object data); + void SetItem(string key, T data); event EventHandler Changing; event EventHandler Changed; diff --git a/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs b/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs new file mode 100644 index 0000000..f2436e3 --- /dev/null +++ b/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs @@ -0,0 +1,46 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; + +namespace Blazored.SessionStorage.JsonConverters +{ + /// + /// The new Json.NET doesn't support Timespan at this time + /// https://github.com/dotnet/corefx/issues/38641 + /// + public class TimespanJsonConverter : JsonConverter + { + /// + /// Format: Days.Hours:Minutes:Seconds:Milliseconds + /// + public const string TimeSpanFormatString = @"d\.hh\:mm\:ss\:FFF"; + + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var s = reader.GetString(); + if (string.IsNullOrWhiteSpace(s)) + { + return TimeSpan.Zero; + } + else + { + TimeSpan parsedTimeSpan; + if (!TimeSpan.TryParseExact(s, TimeSpanFormatString, null, out parsedTimeSpan)) + { + throw new FormatException($"Input timespan is not in an expected format : expected {Regex.Unescape(TimeSpanFormatString)}. Please retrieve this key as a string and parse manually."); + } + else + { + return parsedTimeSpan; + } + } + } + + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + { + var timespanFormatted = $"{value.ToString(TimeSpanFormatString)}"; + writer.WriteStringValue(timespanFormatted); + } + } +} diff --git a/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs b/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs index a76e37f..4bb05e7 100644 --- a/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs +++ b/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs @@ -1,4 +1,7 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.StorageOptions; +using Microsoft.Extensions.DependencyInjection; namespace Blazored.SessionStorage { @@ -8,7 +11,23 @@ public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollecti { return services .AddScoped() - .AddScoped(); + .AddScoped() + .Configure(configureOptions => + { + configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); + }); } + + public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services, Action configure) + { + return services + .AddScoped() + .AddScoped() + .Configure(configureOptions => + { + configure?.Invoke(configureOptions); + configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); + }); + } } } diff --git a/src/Blazored.SessionStorage/SessionStorageService.cs b/src/Blazored.SessionStorage/SessionStorageService.cs index beb04d1..efb0922 100644 --- a/src/Blazored.SessionStorage/SessionStorageService.cs +++ b/src/Blazored.SessionStorage/SessionStorageService.cs @@ -1,4 +1,6 @@ -using Microsoft.JSInterop; +using Blazored.SessionStorage.StorageOptions; +using Microsoft.Extensions.Options; +using Microsoft.JSInterop; using System; using System.Text.Json; using System.Threading.Tasks; @@ -9,14 +11,19 @@ public class SessionStorageService : ISessionStorageService, ISyncSessionStorage { private readonly IJSRuntime _jSRuntime; private readonly IJSInProcessRuntime _jSInProcessRuntime; + private readonly JsonSerializerOptions _jsonOptions; - public SessionStorageService(IJSRuntime jSRuntime) + public event EventHandler Changing; + public event EventHandler Changed; + + public SessionStorageService(IJSRuntime jSRuntime, IOptions options) { - _jSRuntime = jSRuntime; - _jSInProcessRuntime = jSRuntime as IJSInProcessRuntime; + _jSRuntime = jSRuntime; + _jsonOptions = options.Value.JsonSerializerOptions; + _jSInProcessRuntime = jSRuntime as IJSInProcessRuntime; } - public async Task SetItemAsync(string key, object data) + public async Task SetItemAsync(string key, T data) { if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); @@ -24,9 +31,11 @@ public async Task SetItemAsync(string key, object data) var e = await RaiseOnChangingAsync(key, data); if (e.Cancel) - return; - - await _jSRuntime.InvokeAsync("sessionStorage.setItem", key, JsonSerializer.Serialize(data)); + return; + + var serialisedData = JsonSerializer.Serialize(data, _jsonOptions); + + await _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", key, serialisedData); RaiseOnChanged(key, e.OldValue, data); } @@ -39,9 +48,9 @@ public async Task GetItemAsync(string key) var serialisedData = await _jSRuntime.InvokeAsync("sessionStorage.getItem", key); if (serialisedData == null) - return default(T); - - return JsonSerializer.Deserialize(serialisedData); + return default; + + return JsonSerializer.Deserialize(serialisedData, _jsonOptions); } public async Task RemoveItemAsync(string key) @@ -58,7 +67,7 @@ public async Task RemoveItemAsync(string key) public async Task KeyAsync(int index) => await _jSRuntime.InvokeAsync("sessionStorage.key", index); - public void SetItem(string key, object data) + public void SetItem(string key, T data) { if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); @@ -71,7 +80,9 @@ public void SetItem(string key, object data) if (e.Cancel) return; - _jSInProcessRuntime.Invoke("sessionStorage.setItem", key, JsonSerializer.Serialize(data)); + var serialisedData = JsonSerializer.Serialize(data, _jsonOptions); + + _jSInProcessRuntime.InvokeVoid("sessionStorage.setItem", key, serialisedData); RaiseOnChanged(key, e.OldValue, data); } @@ -87,9 +98,9 @@ public T GetItem(string key) var serialisedData = _jSInProcessRuntime.Invoke("sessionStorage.getItem", key); if (serialisedData == null) - return default(T); - - return JsonSerializer.Deserialize(serialisedData); + return default; + + return JsonSerializer.Deserialize(serialisedData, _jsonOptions); } public void RemoveItem(string key) @@ -100,7 +111,7 @@ public void RemoveItem(string key) if (_jSInProcessRuntime == null) throw new InvalidOperationException("IJSInProcessRuntime not available"); - _jSInProcessRuntime.Invoke("sessionStorage.removeItem", key); + _jSInProcessRuntime.InvokeVoid("sessionStorage.removeItem", key); } public void Clear() @@ -108,7 +119,7 @@ public void Clear() if (_jSInProcessRuntime == null) throw new InvalidOperationException("IJSInProcessRuntime not available"); - _jSInProcessRuntime.Invoke("sessionStorage.clear"); + _jSInProcessRuntime.InvokeVoid("sessionStorage.clear"); } public int Length() @@ -127,7 +138,6 @@ public string Key(int index) return _jSInProcessRuntime.Invoke("sessionStorage.key", index); } - public event EventHandler Changing; private async Task RaiseOnChangingAsync(string key, object data) { var e = new ChangingEventArgs @@ -156,7 +166,6 @@ private ChangingEventArgs RaiseOnChangingSync(string key, object data) return e; } - public event EventHandler Changed; private void RaiseOnChanged(string key, object oldValue, object data) { var e = new ChangedEventArgs diff --git a/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs b/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs new file mode 100644 index 0000000..b005f1a --- /dev/null +++ b/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs @@ -0,0 +1,18 @@ +using System.Text.Json; + +namespace Blazored.SessionStorage.StorageOptions +{ + public class SessionStorageOptions + { + public JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions + { + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + IgnoreNullValues = true, + IgnoreReadOnlyProperties = true, + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Skip, + WriteIndented = false + }; + } +} From e4ef5c0e2402748f3661db84168442b1171fc3f8 Mon Sep 17 00:00:00 2001 From: Jan Johansson Date: Thu, 21 May 2020 19:53:14 +0200 Subject: [PATCH 03/20] Fixed typo in documentation --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8a08178..eeaece1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Blazored SessionStorage -A library to provide access to local storage in Blazor applications +A library to provide access to session storage in Blazor applications [![Build Status](https://dev.azure.com/blazored/SessionStorage/_apis/build/status/Blazored.SessionStorage?branchName=master)](https://dev.azure.com/blazored/SessionStorage/_build/latest?definitionId=1&branchName=master) -[![Nuget](https://img.shields.io/nuget/v/blazored.localstorage.svg)](https://www.nuget.org/packages/Blazored.SessionStorage/) +[![Nuget](https://img.shields.io/nuget/v/blazored.sessionstorage.svg)](https://www.nuget.org/packages/Blazored.SessionStorage/) ### Installing @@ -15,7 +15,7 @@ Or via the Visual Studio package manager. ### Setup -You will need to register the local storage services with the service collection in your _Startup.cs_ file in Blazor Server. +You will need to register the session storage services with the service collection in your _Startup.cs_ file in Blazor Server. ```c# public void ConfigureServices(IServiceCollection services) @@ -40,7 +40,7 @@ public static async Task Main(string[] args) ### Configuration -The local storage provides options that can be modified by you at registration in your _Startup.cs_ file in Blazor Server. +The session storage provides options that can be modified by you at registration in your _Startup.cs_ file in Blazor Server. ```c# @@ -69,14 +69,14 @@ public static async Task Main(string[] args) To use Blazored.SessionStorage in Blazor WebAssembly, inject the `ISessionStorageService` per the example below. ```c# -@inject Blazored.SessionStorage.ISessionStorageService localStorage +@inject Blazored.SessionStorage.ISessionStorageService sessionStorage @code { protected override async Task OnInitializedAsync() { - await localStorage.SetItemAsync("name", "John Smith"); - var name = await localStorage.GetItemAsync("name"); + await sessionStorage.SetItemAsync("name", "John Smith"); + var name = await sessionStorage.GetItemAsync("name"); } } @@ -85,14 +85,14 @@ To use Blazored.SessionStorage in Blazor WebAssembly, inject the `ISessionStorag With Blazor WebAssembly you also have the option of a synchronous API, if your use case requires it. You can swap the `ISessionStorageService` for `ISyncStorageService` which allows you to avoid use of `async`/`await`. For either interface, the method names are the same. ```c# -@inject Blazored.SessionStorage.ISyncStorageService localStorage +@inject Blazored.SessionStorage.ISyncStorageService sessionStorage @code { protected override void OnInitialized() { - localStorage.SetItem("name", "John Smith"); - var name = localStorage.GetItem("name"); + sessionStorage.SetItem("name", "John Smith"); + var name = sessionStorage.GetItem("name"); } } @@ -103,14 +103,14 @@ With Blazor WebAssembly you also have the option of a synchronous API, if your u **NOTE:** Due to pre-rendering in Blazor Server you can't perform any JS interop until the `OnAfterRender` lifecycle method. ```c# -@inject Blazored.SessionStorage.ISessionStorageService localStorage +@inject Blazored.SessionStorage.ISessionStorageService sessionStorage @code { protected override async Task OnAfterRenderAsync(bool firstRender) { - await localStorage.SetItemAsync("name", "John Smith"); - var name = await localStorage.GetItemAsync("name"); + await sessionStorage.SetItemAsync("name", "John Smith"); + var name = await sessionStorage.GetItemAsync("name"); } } From c39b8068c765168e236d8c144c8ba56d0accd358 Mon Sep 17 00:00:00 2001 From: Jan Johansson Date: Thu, 21 May 2020 19:56:15 +0200 Subject: [PATCH 04/20] Fixed another typo in documentation --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index eeaece1..fa8fafb 100644 --- a/README.md +++ b/README.md @@ -82,10 +82,10 @@ To use Blazored.SessionStorage in Blazor WebAssembly, inject the `ISessionStorag } ``` -With Blazor WebAssembly you also have the option of a synchronous API, if your use case requires it. You can swap the `ISessionStorageService` for `ISyncStorageService` which allows you to avoid use of `async`/`await`. For either interface, the method names are the same. +With Blazor WebAssembly you also have the option of a synchronous API, if your use case requires it. You can swap the `ISessionStorageService` for `ISyncSessionStorageService` which allows you to avoid use of `async`/`await`. For either interface, the method names are the same. ```c# -@inject Blazored.SessionStorage.ISyncStorageService sessionStorage +@inject Blazored.SessionStorage.ISyncSessionStorageService sessionStorage @code { @@ -127,7 +127,7 @@ The APIs available are: - KeyAsync() - ContainsKeyAsync() -- synchronous via `ISyncStorageService` (Synchronous methods are **only** available in Blazor WebAssembly): +- synchronous via `ISyncSessionStorageService` (Synchronous methods are **only** available in Blazor WebAssembly): - SetItem() - GetItem() - RemoveItem() From 9e9f4fc2f44fabd0845ecc1468e16f3592a02496 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Fri, 22 May 2020 23:31:04 +0100 Subject: [PATCH 05/20] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 19b3826..c7e2638 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,16 +2,18 @@ trigger: - master pool: - vmImage: 'vs2017-win2016' + vmImage: 'windows-latest' variables: buildConfiguration: 'Release' steps: -- task: DotNetCoreInstaller@0 - displayName: 'Installing .NET Core SDK...' +- task: UseDotNet@2 + displayName: 'Use .NET Core sdk' inputs: - version: 3.0.100-rc1-014190 + packageType: sdk + version: 3.1.300 + installationPath: $(Agent.ToolsDirectory)/dotnet - task: NuGetToolInstaller@0 displayName: 'Installing Nuget Tools...' From 4dfecb7fd67d89236ba066abcbb9acb47c75b9f6 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Fri, 22 May 2020 23:36:53 +0100 Subject: [PATCH 06/20] Bumped version number --- src/Blazored.SessionStorage/Blazored.SessionStorage.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj index c6c6963..5c59c0d 100644 --- a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj +++ b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj @@ -6,7 +6,7 @@ Blazored.SessionStorage Blazored.SessionStorage - 1.0.11 + 1.0.12 Chris Sainty A library to provide access to session storage in Blazor applications Copyright 2018 (c) Chris Sainty. All rights reserved. From bcdc6673819ada1e6d6c4559f84fa7cc53946b69 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Wed, 22 Jul 2020 13:32:08 +0100 Subject: [PATCH 07/20] Update to GH Actions --- .github/workflows/ci-main.yml | 110 ++++++++++++++++++ .github/workflows/ci-pr.yml | 30 +++++ .github/workflows/release-drafter.yml | 2 +- Blazored.SessionStorage.sln | 2 +- azure-pipelines.yml | 36 ------ samples/BlazorClientSide/App.razor | 14 --- .../BlazorClientSide/BlazorClientSide.csproj | 23 ---- samples/BlazorClientSide/Pages/_Imports.razor | 1 - samples/BlazorClientSide/Program.cs | 16 --- samples/BlazorClientSide/Startup.cs | 19 --- samples/BlazorClientSide/wwwroot/index.html | 16 --- samples/BlazorWebAssembly/App.razor | 10 ++ .../BlazorWebAssembly.csproj | 19 +++ .../Pages/Index.razor | 0 .../Pages/Sync.razor | 0 samples/BlazorWebAssembly/Program.cs | 18 +++ .../Shared/MainLayout.razor | 0 .../Shared/NavMenu.razor | 4 +- .../_Imports.razor | 5 +- samples/BlazorWebAssembly/wwwroot/.nojekyll | 1 + samples/BlazorWebAssembly/wwwroot/404.html | 38 ++++++ .../wwwroot/css/bootstrap/bootstrap.min.css | 0 .../css/bootstrap/bootstrap.min.css.map | 0 .../wwwroot/css/open-iconic/FONT-LICENSE | 0 .../wwwroot/css/open-iconic/ICON-LICENSE | 0 .../wwwroot/css/open-iconic/README.md | 0 .../font/css/open-iconic-bootstrap.min.css | 0 .../open-iconic/font/fonts/open-iconic.eot | Bin .../open-iconic/font/fonts/open-iconic.otf | Bin .../open-iconic/font/fonts/open-iconic.svg | 0 .../open-iconic/font/fonts/open-iconic.ttf | Bin .../open-iconic/font/fonts/open-iconic.woff | Bin .../wwwroot/css/site.css | 0 samples/BlazorWebAssembly/wwwroot/favicon.ico | Bin 0 -> 32038 bytes samples/BlazorWebAssembly/wwwroot/index.html | 79 +++++++++++++ .../BlazorWebAssembly/wwwroot/js/decode.js | 1 + .../Blazored.SessionStorage.csproj | 34 ++++-- src/Blazored.SessionStorage/icon.png | Bin 0 -> 44398 bytes 38 files changed, 336 insertions(+), 142 deletions(-) create mode 100644 .github/workflows/ci-main.yml create mode 100644 .github/workflows/ci-pr.yml delete mode 100644 azure-pipelines.yml delete mode 100644 samples/BlazorClientSide/App.razor delete mode 100644 samples/BlazorClientSide/BlazorClientSide.csproj delete mode 100644 samples/BlazorClientSide/Pages/_Imports.razor delete mode 100644 samples/BlazorClientSide/Program.cs delete mode 100644 samples/BlazorClientSide/Startup.cs delete mode 100644 samples/BlazorClientSide/wwwroot/index.html create mode 100644 samples/BlazorWebAssembly/App.razor create mode 100644 samples/BlazorWebAssembly/BlazorWebAssembly.csproj rename samples/{BlazorClientSide => BlazorWebAssembly}/Pages/Index.razor (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/Pages/Sync.razor (100%) create mode 100644 samples/BlazorWebAssembly/Program.cs rename samples/{BlazorClientSide => BlazorWebAssembly}/Shared/MainLayout.razor (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/Shared/NavMenu.razor (86%) rename samples/{BlazorClientSide => BlazorWebAssembly}/_Imports.razor (62%) create mode 100644 samples/BlazorWebAssembly/wwwroot/.nojekyll create mode 100644 samples/BlazorWebAssembly/wwwroot/404.html rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/bootstrap/bootstrap.min.css (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/bootstrap/bootstrap.min.css.map (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/FONT-LICENSE (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/ICON-LICENSE (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/README.md (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/font/fonts/open-iconic.eot (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/font/fonts/open-iconic.otf (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/font/fonts/open-iconic.svg (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/open-iconic/font/fonts/open-iconic.woff (100%) rename samples/{BlazorClientSide => BlazorWebAssembly}/wwwroot/css/site.css (100%) create mode 100644 samples/BlazorWebAssembly/wwwroot/favicon.ico create mode 100644 samples/BlazorWebAssembly/wwwroot/index.html create mode 100644 samples/BlazorWebAssembly/wwwroot/js/decode.js create mode 100644 src/Blazored.SessionStorage/icon.png diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml new file mode 100644 index 0000000..b827250 --- /dev/null +++ b/.github/workflows/ci-main.yml @@ -0,0 +1,110 @@ +name: Build & Test Main + +on: + push: + branches: [ main ] + release: + types: [ published ] + +env: + NETCORE_VERSION: '3.1.301' + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + PROJECT_NAME: SessionStorage + GITHUB_FEED: https://nuget.pkg.github.com/Blazored/ + GITHUB_USER: chrissainty + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NUGET_FEED: https://api.nuget.org/v3/index.json + NUGET_KEY: ${{ secrets.NUGET_KEY }} + +jobs: + build: + name: Build, test and pack + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup .NET Core ${{ env.NETCORE_VERSION }} + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.NETCORE_VERSION }} + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Restore + run: dotnet restore + + - name: Build + run: dotnet build -c Release --no-restore src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.csproj + + - name: Test + run: dotnet test -c Release + + - name: Pack + run: dotnet pack -v normal -c Release --no-restore --include-symbols --include-source -p:PackageVersion=$GITHUB_RUN_ID src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.*proj + + - name: Publish Sample Site + run: dotnet publish -c Release samples/BlazorWebAssembly/BlazorWebAssembly.csproj + + - name: Rewrite base href + uses: SteveSandersonMS/ghaction-rewrite-base-href@v1 + with: + html_path: samples/BlazorWebAssembly/bin/Release/netstandard2.1/publish/wwwroot/index.html + base_href: /${{ env.PROJECT_NAME }}/ + + - name: Deploy to Github Pages + uses: JamesIves/github-pages-deploy-action@releases/v3 + with: + ACCESS_TOKEN: $GITHUB_TOKEN + BASE_BRANCH: main # The branch the action should deploy from. + BRANCH: gh-pages # The branch the action should deploy to. + FOLDER: samples/BlazorWebAssembly/bin/Release/netstandard2.1/publish/wwwroot # The folder the action should deploy. + SINGLE_COMMIT: true + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: nupkg + path: ./src/Blazored.${{ env.PROJECT_NAME }}/bin/Release/*.nupkg + prerelease: + needs: build + if: github.event_name != 'release' + runs-on: ubuntu-latest + steps: + - name: Download Artifact + uses: actions/download-artifact@v1 + with: + name: nupkg + - name: Push to GitHub Feed + run: | + for f in ./nupkg/*.nupkg + do + curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED + done + deploy: + needs: build + if: github.event_name == 'release' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.NETCORE_VERSION }} + - name: Create Release NuGet package + run: | + arrTag=(${GITHUB_REF//\// }) + VERSION="${arrTag[2]}" + echo Version: $VERSION + VERSION="${VERSION//v}" + echo Clean Version: $VERSION + dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.*proj + - name: Push to GitHub Feed + run: | + for f in ./nupkg/*.nupkg + do + curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED + done + - name: Push to NuGet Feed + run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml new file mode 100644 index 0000000..bdc0578 --- /dev/null +++ b/.github/workflows/ci-pr.yml @@ -0,0 +1,30 @@ +name: Build & Test PR + +on: + pull_request: + branches: [ main ] + +env: + PROJECT_NAME: SessionStorage + NETCORE_VERSION: '3.1.301' + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Setting up .NET Core SDK ${{ env.NETCORE_VERSION }}... + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.NETCORE_VERSION }} + + - name: Restoring packages... + run: dotnet restore + + - name: Building project... + run: dotnet build --configuration Release --no-restore src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.csproj + + - name: Testing... + run: dotnet test --no-restore --verbosity normal \ No newline at end of file diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 3fb8ef3..d706705 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -3,7 +3,7 @@ name: Release Drafter on: push: branches: - - master + - main jobs: update_release_draft: diff --git a/Blazored.SessionStorage.sln b/Blazored.SessionStorage.sln index 02b5d7e..126452d 100644 --- a/Blazored.SessionStorage.sln +++ b/Blazored.SessionStorage.sln @@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazored.SessionStorage", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F20B2AAB-FB71-4A1C-82FE-F1750B922BF6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorClientSide", "samples\BlazorClientSide\BlazorClientSide.csproj", "{D6953CE0-6AD5-434C-99AA-7FF1E26724D4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWebAssembly", "samples\BlazorWebAssembly\BlazorWebAssembly.csproj", "{D6953CE0-6AD5-434C-99AA-7FF1E26724D4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServerSide", "samples\BlazorServerSide\BlazorServerSide.csproj", "{767D657A-5410-4714-9477-99929D3FF709}" EndProject diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index c7e2638..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,36 +0,0 @@ -trigger: -- master - -pool: - vmImage: 'windows-latest' - -variables: - buildConfiguration: 'Release' - -steps: -- task: UseDotNet@2 - displayName: 'Use .NET Core sdk' - inputs: - packageType: sdk - version: 3.1.300 - installationPath: $(Agent.ToolsDirectory)/dotnet - -- task: NuGetToolInstaller@0 - displayName: 'Installing Nuget Tools...' - inputs: - versionSpec: '4.9.2' - -- script: dotnet build --configuration $(buildConfiguration) src/Blazored.SessionStorage/Blazored.SessionStorage.csproj - displayName: 'Building $(buildConfiguration)...' - -- task: DotNetCoreCLI@2 - displayName: 'Generating Nuget Package... ' - inputs: - command: pack - - arguments: --no-build - packagesToPack: src/Blazored.SessionStorage/Blazored.SessionStorage.csproj - configuration: $(buildConfiguration) - -- task: PublishBuildArtifacts@1 - displayName: 'Publishing Build Artifacts...' diff --git a/samples/BlazorClientSide/App.razor b/samples/BlazorClientSide/App.razor deleted file mode 100644 index 39b52f6..0000000 --- a/samples/BlazorClientSide/App.razor +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - -

Sorry, there's nothing at this address.

-
-
-
\ No newline at end of file diff --git a/samples/BlazorClientSide/BlazorClientSide.csproj b/samples/BlazorClientSide/BlazorClientSide.csproj deleted file mode 100644 index 85df72f..0000000 --- a/samples/BlazorClientSide/BlazorClientSide.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - netstandard2.0 - - https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; - https://dotnet.myget.org/F/blazor-dev/api/v3/index.json; - - 7.3 - 3.0 - - - - - - - - - - - - - diff --git a/samples/BlazorClientSide/Pages/_Imports.razor b/samples/BlazorClientSide/Pages/_Imports.razor deleted file mode 100644 index 0f24eda..0000000 --- a/samples/BlazorClientSide/Pages/_Imports.razor +++ /dev/null @@ -1 +0,0 @@ -@layout MainLayout diff --git a/samples/BlazorClientSide/Program.cs b/samples/BlazorClientSide/Program.cs deleted file mode 100644 index 755f692..0000000 --- a/samples/BlazorClientSide/Program.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.AspNetCore.Blazor.Hosting; - -namespace BlazorClientSide -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) => - BlazorWebAssemblyHost.CreateDefaultBuilder() - .UseBlazorStartup(); - } -} diff --git a/samples/BlazorClientSide/Startup.cs b/samples/BlazorClientSide/Startup.cs deleted file mode 100644 index fb812ae..0000000 --- a/samples/BlazorClientSide/Startup.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Blazored.SessionStorage; -using Microsoft.AspNetCore.Components.Builder; -using Microsoft.Extensions.DependencyInjection; - -namespace BlazorClientSide -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddBlazoredSessionStorage(); - } - - public void Configure(IComponentsApplicationBuilder app) - { - app.AddComponent("app"); - } - } -} diff --git a/samples/BlazorClientSide/wwwroot/index.html b/samples/BlazorClientSide/wwwroot/index.html deleted file mode 100644 index 3ea544f..0000000 --- a/samples/BlazorClientSide/wwwroot/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Blazored SessionStorage - - - - - - Loading... - - - - diff --git a/samples/BlazorWebAssembly/App.razor b/samples/BlazorWebAssembly/App.razor new file mode 100644 index 0000000..9f62581 --- /dev/null +++ b/samples/BlazorWebAssembly/App.razor @@ -0,0 +1,10 @@ + + + + + + +

Sorry, but there's nothing here!

+
+
+
\ No newline at end of file diff --git a/samples/BlazorWebAssembly/BlazorWebAssembly.csproj b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj new file mode 100644 index 0000000..144cb19 --- /dev/null +++ b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.1 + 3.0 + + + + + + + + + + + + + + diff --git a/samples/BlazorClientSide/Pages/Index.razor b/samples/BlazorWebAssembly/Pages/Index.razor similarity index 100% rename from samples/BlazorClientSide/Pages/Index.razor rename to samples/BlazorWebAssembly/Pages/Index.razor diff --git a/samples/BlazorClientSide/Pages/Sync.razor b/samples/BlazorWebAssembly/Pages/Sync.razor similarity index 100% rename from samples/BlazorClientSide/Pages/Sync.razor rename to samples/BlazorWebAssembly/Pages/Sync.razor diff --git a/samples/BlazorWebAssembly/Program.cs b/samples/BlazorWebAssembly/Program.cs new file mode 100644 index 0000000..3bf7221 --- /dev/null +++ b/samples/BlazorWebAssembly/Program.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Blazored.SessionStorage; + +namespace BlazorWebAssembly +{ + public class Program + { + public static async Task Main(string[] args) + { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("app"); + builder.Services.AddBlazoredSessionStorage(); + + await builder.Build().RunAsync(); + } + } +} diff --git a/samples/BlazorClientSide/Shared/MainLayout.razor b/samples/BlazorWebAssembly/Shared/MainLayout.razor similarity index 100% rename from samples/BlazorClientSide/Shared/MainLayout.razor rename to samples/BlazorWebAssembly/Shared/MainLayout.razor diff --git a/samples/BlazorClientSide/Shared/NavMenu.razor b/samples/BlazorWebAssembly/Shared/NavMenu.razor similarity index 86% rename from samples/BlazorClientSide/Shared/NavMenu.razor rename to samples/BlazorWebAssembly/Shared/NavMenu.razor index 4eca3b8..2a22094 100644 --- a/samples/BlazorClientSide/Shared/NavMenu.razor +++ b/samples/BlazorWebAssembly/Shared/NavMenu.razor @@ -1,5 +1,5 @@ integrated" lang="enPortuguesesubstituteindividualimpossiblemultimediaalmost allpx solid #apart fromsubject toin Englishcriticizedexcept forguidelinesoriginallyremarkablethe secondh2 class="collection\r\nfunctionvisibilitythe use ofvolunteersattractionunder the threatened*\nevaluationemphasizedaccessiblesuccessionalong withMeanwhile,industries
has becomeaspects ofTelevisionsufficientbasketballboth sidescontinuingan articleadventureshis mothermanchesterprinciplesparticularcommentaryeffects ofdecided topublishersJournal ofdifficultyfacilitateacceptablestyle.css"\tfunction innovation>Copyrightsituationswould havebusinessesDictionarystatementsoften usedpersistentin Januarycomprising\n\tdiplomaticcontainingperformingextensionsmay not beconcept of onclick="It is alsofinancial making theLuxembourgadditionalare calledengaged in"script");but it waselectroniconsubmit="\n\x3c!-- End electricalofficiallysuggestiontop of theunlike theAustralianOriginallyreferences\n\r\nrecognisedinitializelimited toAlexandriaretirementAdventuresfour years\n\n<!-- increasingdecorationh3 class="origins ofobligationregulationclassified(function(advantagesbeing the historiansthe publicmany yearswhich wereover time,synonymouscontent">\npresumablyhis familyuserAgent.unexpectedincluding challengeda minorityundefined"belongs totaken fromin Octoberposition: said to bereligious Federation rowspan="only a fewmeant thatled to the--\x3e\r\n
Archbishop class="nobeing usedapproachesprivilegesnoscript>\nresults inmay be theEaster eggmechanismsreasonablePopulationCollectionselected">noscript>\r/index.phparrival of-jssdk\'));managed toincompletecasualtiescompletionChristiansSeptember arithmeticproceduresmight haveProductionit appearsPhilosophyfriendshipleading togiving thetoward theguaranteeddocumentedcolor:#000video gamecommissionreflectingchange theassociatedsans-serifonkeypress; padding:He was theunderlyingtypically , and the srcElementsuccessivesince the should be networkingaccountinguse of thelower thanshows that\n\t\tcomplaintscontinuousquantitiesastronomerhe did notdue to itsapplied toan averageefforts tothe futureattempt toTherefore,capabilityRepublicanwas formedElectronickilometerschallengespublishingthe formerindigenousdirectionssubsidiaryconspiracydetails ofand in theaffordablesubstancesreason forconventionitemtype="absolutelysupposedlyremained aattractivetravellingseparatelyfocuses onelementaryapplicablefound thatstylesheetmanuscriptstands for no-repeat(sometimesCommercialin Americaundertakenquarter ofan examplepersonallyindex.php?\npercentagebest-knowncreating a" dir="ltrLieutenant\n
is said tostructuralreferendummost oftena separate->\n
\r + + diff --git a/samples/BlazorServerSide/Program.cs b/samples/BlazorServer/Program.cs similarity index 66% rename from samples/BlazorServerSide/Program.cs rename to samples/BlazorServer/Program.cs index 23a8797..1f2b71a 100644 --- a/samples/BlazorServerSide/Program.cs +++ b/samples/BlazorServer/Program.cs @@ -1,15 +1,7 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -namespace BlazorServerSide +namespace BlazorServer { public class Program { diff --git a/samples/BlazorServer/Shared/MainLayout.razor b/samples/BlazorServer/Shared/MainLayout.razor new file mode 100644 index 0000000..2281c4f --- /dev/null +++ b/samples/BlazorServer/Shared/MainLayout.razor @@ -0,0 +1,17 @@ +@inherits LayoutComponentBase + +
+ + +
+
+ About +
+ +
+ @Body +
+
+
diff --git a/samples/BlazorServer/Shared/MainLayout.razor.css b/samples/BlazorServer/Shared/MainLayout.razor.css new file mode 100644 index 0000000..4ad9519 --- /dev/null +++ b/samples/BlazorServer/Shared/MainLayout.razor.css @@ -0,0 +1,70 @@ +.page { + position: relative; + display: flex; + flex-direction: column; +} + +.main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + } + + .top-row a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row:not(.auth) { + display: none; + } + + .top-row.auth { + justify-content: space-between; + } + + .top-row a, .top-row .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .main > div { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} diff --git a/samples/BlazorServerSide/Shared/NavMenu.razor b/samples/BlazorServer/Shared/NavMenu.razor similarity index 100% rename from samples/BlazorServerSide/Shared/NavMenu.razor rename to samples/BlazorServer/Shared/NavMenu.razor diff --git a/samples/BlazorServer/Shared/NavMenu.razor.css b/samples/BlazorServer/Shared/NavMenu.razor.css new file mode 100644 index 0000000..f7f4dbe --- /dev/null +++ b/samples/BlazorServer/Shared/NavMenu.razor.css @@ -0,0 +1,62 @@ +.navbar-toggler { + background-color: rgba(255, 255, 255, 0.1); +} + +.top-row { + height: 3.5rem; + background-color: rgba(0,0,0,0.4); +} + +.navbar-brand { + font-size: 1.1rem; +} + +.oi { + width: 2rem; + font-size: 1.1rem; + vertical-align: text-top; + top: -2px; +} + +.nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; +} + + .nav-item:first-of-type { + padding-top: 1rem; + } + + .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .nav-item ::deep a { + color: #d7d7d7; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + } + + .nav-item ::deep a.active { + background-color: rgba(255,255,255,0.25); + color: white; + } + + .nav-item ::deep a:hover { + background-color: rgba(255,255,255,0.1); + color: white; + } + +@media (min-width: 641px) { + .navbar-toggler { + display: none; + } + + .collapse { + /* Never collapse the sidebar for wide screens */ + display: block; + } +} diff --git a/samples/BlazorServerSide/Startup.cs b/samples/BlazorServer/Startup.cs similarity index 62% rename from samples/BlazorServerSide/Startup.cs rename to samples/BlazorServer/Startup.cs index 64fb58b..afb1033 100644 --- a/samples/BlazorServerSide/Startup.cs +++ b/samples/BlazorServer/Startup.cs @@ -3,18 +3,31 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Blazored.SessionStorage; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Blazored.SessionStorage.Serialization; +using Newtonsoft.Json; +using System; -namespace BlazorServerSide +namespace BlazorServer { public class Startup { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); - services.AddServerSideBlazor(); + services.AddServerSideBlazor(config => config.DetailedErrors = true); services.AddBlazoredSessionStorage(); + services.Replace(ServiceDescriptor.Scoped()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -26,12 +39,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) } else { + app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); - app.UseStaticFiles(); app.UseRouting(); @@ -43,4 +56,13 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) }); } } + + public class NewtonSoftJsonSerializer : IJsonSerializer + { + public T Deserialize(string text) + =>JsonConvert.DeserializeObject(text); + + public string Serialize(T obj) + => JsonConvert.SerializeObject(obj); + } } diff --git a/samples/BlazorServerSide/_Imports.razor b/samples/BlazorServer/_Imports.razor similarity index 51% rename from samples/BlazorServerSide/_Imports.razor rename to samples/BlazorServer/_Imports.razor index 020af93..f71dbdc 100644 --- a/samples/BlazorServerSide/_Imports.razor +++ b/samples/BlazorServer/_Imports.razor @@ -1,8 +1,12 @@ @using System.Net.Http +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop -@using BlazorServerSide.Shared +@using BlazorServer +@using BlazorServer.Shared @using Blazored.SessionStorage diff --git a/samples/BlazorServerSide/appsettings.Development.json b/samples/BlazorServer/appsettings.Development.json similarity index 100% rename from samples/BlazorServerSide/appsettings.Development.json rename to samples/BlazorServer/appsettings.Development.json diff --git a/samples/BlazorServerSide/appsettings.json b/samples/BlazorServer/appsettings.json similarity index 100% rename from samples/BlazorServerSide/appsettings.json rename to samples/BlazorServer/appsettings.json diff --git a/samples/BlazorServerSide/wwwroot/css/bootstrap/bootstrap.min.css b/samples/BlazorServer/wwwroot/css/bootstrap/bootstrap.min.css similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/bootstrap/bootstrap.min.css rename to samples/BlazorServer/wwwroot/css/bootstrap/bootstrap.min.css diff --git a/samples/BlazorServerSide/wwwroot/css/bootstrap/bootstrap.min.css.map b/samples/BlazorServer/wwwroot/css/bootstrap/bootstrap.min.css.map similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/bootstrap/bootstrap.min.css.map rename to samples/BlazorServer/wwwroot/css/bootstrap/bootstrap.min.css.map diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/FONT-LICENSE b/samples/BlazorServer/wwwroot/css/open-iconic/FONT-LICENSE similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/FONT-LICENSE rename to samples/BlazorServer/wwwroot/css/open-iconic/FONT-LICENSE diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/ICON-LICENSE b/samples/BlazorServer/wwwroot/css/open-iconic/ICON-LICENSE similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/ICON-LICENSE rename to samples/BlazorServer/wwwroot/css/open-iconic/ICON-LICENSE diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/README.md b/samples/BlazorServer/wwwroot/css/open-iconic/README.md similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/README.md rename to samples/BlazorServer/wwwroot/css/open-iconic/README.md diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css b/samples/BlazorServer/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css rename to samples/BlazorServer/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.eot b/samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.eot similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.eot rename to samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.eot diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.otf b/samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.otf similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.otf rename to samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.otf diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.svg b/samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.svg similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.svg rename to samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.svg diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf b/samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf rename to samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf diff --git a/samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.woff b/samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.woff similarity index 100% rename from samples/BlazorServerSide/wwwroot/css/open-iconic/font/fonts/open-iconic.woff rename to samples/BlazorServer/wwwroot/css/open-iconic/font/fonts/open-iconic.woff diff --git a/samples/BlazorServer/wwwroot/css/site.css b/samples/BlazorServer/wwwroot/css/site.css new file mode 100644 index 0000000..caebf2a --- /dev/null +++ b/samples/BlazorServer/wwwroot/css/site.css @@ -0,0 +1,50 @@ +@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); + +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +a, .btn-link { + color: #0366d6; +} + +.btn-primary { + color: #fff; + background-color: #1b6ec2; + border-color: #1861ac; +} + +.content { + padding-top: 1.1rem; +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid red; +} + +.validation-message { + color: red; +} + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/samples/BlazorServerSide/wwwroot/favicon.ico b/samples/BlazorServer/wwwroot/favicon.ico similarity index 100% rename from samples/BlazorServerSide/wwwroot/favicon.ico rename to samples/BlazorServer/wwwroot/favicon.ico diff --git a/samples/BlazorServerSide/Pages/_Host.cshtml b/samples/BlazorServerSide/Pages/_Host.cshtml deleted file mode 100644 index 469597c..0000000 --- a/samples/BlazorServerSide/Pages/_Host.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@page "/" -@namespace BlazorServerSide.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers - - - - - - - Blazored LocalStorage - Server-Side - - - - - - - - - - - @(await Html.RenderComponentAsync(RenderMode.Server)) - - - - diff --git a/samples/BlazorServerSide/Pages/_Imports.razor b/samples/BlazorServerSide/Pages/_Imports.razor deleted file mode 100644 index 0f24eda..0000000 --- a/samples/BlazorServerSide/Pages/_Imports.razor +++ /dev/null @@ -1 +0,0 @@ -@layout MainLayout diff --git a/samples/BlazorServerSide/Shared/MainLayout.razor b/samples/BlazorServerSide/Shared/MainLayout.razor deleted file mode 100644 index 97e764d..0000000 --- a/samples/BlazorServerSide/Shared/MainLayout.razor +++ /dev/null @@ -1,15 +0,0 @@ -@inherits LayoutComponentBase - - - -
-
- About -
- -
- @Body -
-
diff --git a/samples/BlazorServerSide/wwwroot/css/site.css b/samples/BlazorServerSide/wwwroot/css/site.css deleted file mode 100644 index 8b2f196..0000000 --- a/samples/BlazorServerSide/wwwroot/css/site.css +++ /dev/null @@ -1,154 +0,0 @@ -@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); - -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -app { - position: relative; - display: flex; - flex-direction: column; -} - -.top-row { - height: 3.5rem; - display: flex; - align-items: center; -} - -.main { - flex: 1; -} - - .main .top-row { - background-color: #e6e6e6; - border-bottom: 1px solid #d6d5d5; - } - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - - .sidebar .top-row { - background-color: rgba(0,0,0,0.4); - } - - .sidebar .navbar-brand { - font-size: 1.1rem; - } - - .sidebar .oi { - width: 2rem; - font-size: 1.1rem; - vertical-align: text-top; - top: -2px; - } - -.nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; -} - - .nav-item:first-of-type { - padding-top: 1rem; - } - - .nav-item:last-of-type { - padding-bottom: 1rem; - } - - .nav-item a { - color: #d7d7d7; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - } - - .nav-item a.active { - background-color: rgba(255,255,255,0.25); - color: white; - } - - .nav-item a:hover { - background-color: rgba(255,255,255,0.1); - color: white; - } - -.content { - padding-top: 1.1rem; -} - -.navbar-toggler { - background-color: rgba(255, 255, 255, 0.1); -} - -.valid.modified:not([type=checkbox]) { - outline: 1px solid #26b050; -} - -.invalid { - outline: 1px solid red; -} - -.validation-message { - color: red; -} - -#blazor-error-ui { - background: lightyellow; - bottom: 0; - box-shadow: 0 -1px 2px rgba(83, 64, 64, 0.2); - display: none; - left: 0; - padding: 0.6rem 1.25rem 0.7rem 1.25rem; - position: fixed; - width: 100%; - z-index: 1000; -} - - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } - -@media (max-width: 767.98px) { - .main .top-row { - display: none; - } -} - -@media (min-width: 768px) { - app { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .main .top-row { - position: sticky; - top: 0; - } - - .main > div { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } - - .navbar-toggler { - display: none; - } - - .sidebar .collapse { - /* Never collapse the sidebar for wide screens */ - display: block; - } -} diff --git a/samples/BlazorWebAssembly/App.razor b/samples/BlazorWebAssembly/App.razor index 9f62581..b941644 100644 --- a/samples/BlazorWebAssembly/App.razor +++ b/samples/BlazorWebAssembly/App.razor @@ -1,10 +1,10 @@ - + - + - -

Sorry, but there's nothing here!

+ +

Sorry, there's nothing at this address.

-
\ No newline at end of file +
diff --git a/samples/BlazorWebAssembly/BlazorWebAssembly.csproj b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj index 144cb19..7011596 100644 --- a/samples/BlazorWebAssembly/BlazorWebAssembly.csproj +++ b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj @@ -1,15 +1,13 @@ - + - netstandard2.1 - 3.0 + net5.0 - - - - + + + diff --git a/samples/BlazorWebAssembly/Pages/Index.razor b/samples/BlazorWebAssembly/Pages/Index.razor index b94913b..31293dc 100644 --- a/samples/BlazorWebAssembly/Pages/Index.razor +++ b/samples/BlazorWebAssembly/Pages/Index.razor @@ -10,9 +10,9 @@
Add Item to session storage
- +
- +
@@ -28,7 +28,7 @@
-
+
Value Read from session storage
@@ -39,17 +39,32 @@
Items in session storage
@ItemsInSessionStorage
+ +
+
Item exist in session storage
@(ItemExist) +
+
+ +
+ +
+
String Read from session storage
+ @StringFromSessionStorage +
@code { string NameFromSessionStorage { get; set; } + string StringFromSessionStorage { get; set; } int ItemsInSessionStorage { get; set; } string Name { get; set; } + bool ItemExist { get; set; } protected override async Task OnInitializedAsync() { await GetNameFromSessionStorage(); + await GetStringFromSessionStorage(); await GetSessionStorageLength(); sessionStorage.Changed += (sender, e) => @@ -62,6 +77,7 @@ { await sessionStorage.SetItemAsync("name", Name); await GetNameFromSessionStorage(); + await GetStringFromSessionStorage(); await GetSessionStorageLength(); Name = ""; @@ -69,11 +85,29 @@ async Task GetNameFromSessionStorage() { - NameFromSessionStorage = await sessionStorage.GetItemAsync("name"); + try + { + NameFromSessionStorage = await sessionStorage.GetItemAsync("name"); + + if (string.IsNullOrEmpty(NameFromSessionStorage)) + { + NameFromSessionStorage = "Nothing Saved"; + } + } + catch(Exception) + { + Console.WriteLine("error reading 'name'"); + } + } + + + async Task GetStringFromSessionStorage() + { + StringFromSessionStorage = await sessionStorage.GetItemAsStringAsync("name"); - if (string.IsNullOrEmpty(NameFromSessionStorage)) + if (string.IsNullOrEmpty(StringFromSessionStorage)) { - NameFromSessionStorage = "Nothing Saved"; + StringFromSessionStorage = "Nothing Saved"; } } @@ -81,6 +115,7 @@ { await sessionStorage.RemoveItemAsync("name"); await GetNameFromSessionStorage(); + await GetStringFromSessionStorage(); await GetSessionStorageLength(); } @@ -90,6 +125,7 @@ await sessionStorage.ClearAsync(); Console.WriteLine("Getting name from session storage..."); await GetNameFromSessionStorage(); + await GetStringFromSessionStorage(); Console.WriteLine("Calling Get Length..."); await GetSessionStorageLength(); } @@ -98,6 +134,7 @@ { Console.WriteLine(await sessionStorage.LengthAsync()); ItemsInSessionStorage = await sessionStorage.LengthAsync(); + ItemExist = await sessionStorage.ContainKeyAsync("name"); } } diff --git a/samples/BlazorWebAssembly/Pages/Sync.razor b/samples/BlazorWebAssembly/Pages/Sync.razor index d625680..59a5f03 100644 --- a/samples/BlazorWebAssembly/Pages/Sync.razor +++ b/samples/BlazorWebAssembly/Pages/Sync.razor @@ -37,17 +37,31 @@
Items in session storage
@ItemsInSessionStorage
+
+
Item exist in session storage
@(ItemExist) +
+
+ +
+ +
+
String Read from session storage
+ @StringFromSessionStorage +
@code { string NameFromSessionStorage { get; set; } + string StringFromSessionStorage { get; set; } int ItemsInSessionStorage { get; set; } string Name { get; set; } + bool ItemExist { get; set; } protected override void OnInitialized() { GetNameFromSessionStorage(); + GetStringFromSessionStorage(); GetSessionStorageLength(); sessionStorage.Changed += (sender, e) => @@ -58,8 +72,10 @@ void SaveName() { + Console.WriteLine(Name); sessionStorage.SetItem("name", Name); GetNameFromSessionStorage(); + GetStringFromSessionStorage(); GetSessionStorageLength(); Name = ""; @@ -67,11 +83,28 @@ void GetNameFromSessionStorage() { - NameFromSessionStorage = sessionStorage.GetItem("name"); + try + { + NameFromSessionStorage = sessionStorage.GetItem("name"); + + if (string.IsNullOrEmpty(NameFromSessionStorage)) + { + NameFromSessionStorage = "Nothing Saved"; + } + } + catch (Exception) + { + Console.WriteLine("error reading 'name'"); + } + } + + void GetStringFromSessionStorage() + { + StringFromSessionStorage = sessionStorage.GetItemAsString("name"); - if (string.IsNullOrEmpty(NameFromSessionStorage)) + if (string.IsNullOrEmpty(StringFromSessionStorage)) { - NameFromSessionStorage = "Nothing Saved"; + StringFromSessionStorage = "Nothing Saved"; } } @@ -79,6 +112,7 @@ { sessionStorage.RemoveItem("name"); GetNameFromSessionStorage(); + GetStringFromSessionStorage(); GetSessionStorageLength(); } @@ -88,6 +122,7 @@ sessionStorage.Clear(); Console.WriteLine("Getting name from session storage..."); GetNameFromSessionStorage(); + GetStringFromSessionStorage(); Console.WriteLine("Calling Get Length..."); GetSessionStorageLength(); } @@ -96,6 +131,7 @@ { Console.WriteLine(sessionStorage.Length()); ItemsInSessionStorage = sessionStorage.Length(); + ItemExist = sessionStorage.ContainKey("name"); } } diff --git a/samples/BlazorWebAssembly/Program.cs b/samples/BlazorWebAssembly/Program.cs index 3bf7221..0fff24f 100644 --- a/samples/BlazorWebAssembly/Program.cs +++ b/samples/BlazorWebAssembly/Program.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using Blazored.SessionStorage; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Blazored.SessionStorage; +using System.Threading.Tasks; namespace BlazorWebAssembly { @@ -9,7 +9,8 @@ public class Program public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.RootComponents.Add("app"); + builder.RootComponents.Add("#app"); + builder.Services.AddBlazoredSessionStorage(); await builder.Build().RunAsync(); diff --git a/samples/BlazorWebAssembly/Shared/MainLayout.razor b/samples/BlazorWebAssembly/Shared/MainLayout.razor index 902e3ed..a76e097 100644 --- a/samples/BlazorWebAssembly/Shared/MainLayout.razor +++ b/samples/BlazorWebAssembly/Shared/MainLayout.razor @@ -1,15 +1,17 @@ @inherits LayoutComponentBase - - -
-
- About +
+ -
- @Body +
+
+ About +
+ +
+ @Body +
diff --git a/samples/BlazorWebAssembly/Shared/MainLayout.razor.css b/samples/BlazorWebAssembly/Shared/MainLayout.razor.css new file mode 100644 index 0000000..4ad9519 --- /dev/null +++ b/samples/BlazorWebAssembly/Shared/MainLayout.razor.css @@ -0,0 +1,70 @@ +.page { + position: relative; + display: flex; + flex-direction: column; +} + +.main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + } + + .top-row a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row:not(.auth) { + display: none; + } + + .top-row.auth { + justify-content: space-between; + } + + .top-row a, .top-row .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .main > div { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} diff --git a/samples/BlazorWebAssembly/Shared/NavMenu.razor b/samples/BlazorWebAssembly/Shared/NavMenu.razor index 2a22094..4869261 100644 --- a/samples/BlazorWebAssembly/Shared/NavMenu.razor +++ b/samples/BlazorWebAssembly/Shared/NavMenu.razor @@ -5,7 +5,7 @@
-
+
@code { - bool collapseNavMenu = true; + private bool collapseNavMenu = true; - void ToggleNavMenu() + private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; + + private void ToggleNavMenu() { collapseNavMenu = !collapseNavMenu; } diff --git a/samples/BlazorWebAssembly/Shared/NavMenu.razor.css b/samples/BlazorWebAssembly/Shared/NavMenu.razor.css new file mode 100644 index 0000000..f7f4dbe --- /dev/null +++ b/samples/BlazorWebAssembly/Shared/NavMenu.razor.css @@ -0,0 +1,62 @@ +.navbar-toggler { + background-color: rgba(255, 255, 255, 0.1); +} + +.top-row { + height: 3.5rem; + background-color: rgba(0,0,0,0.4); +} + +.navbar-brand { + font-size: 1.1rem; +} + +.oi { + width: 2rem; + font-size: 1.1rem; + vertical-align: text-top; + top: -2px; +} + +.nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; +} + + .nav-item:first-of-type { + padding-top: 1rem; + } + + .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .nav-item ::deep a { + color: #d7d7d7; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + } + + .nav-item ::deep a.active { + background-color: rgba(255,255,255,0.25); + color: white; + } + + .nav-item ::deep a:hover { + background-color: rgba(255,255,255,0.1); + color: white; + } + +@media (min-width: 641px) { + .navbar-toggler { + display: none; + } + + .collapse { + /* Never collapse the sidebar for wide screens */ + display: block; + } +} diff --git a/samples/BlazorWebAssembly/_Imports.razor b/samples/BlazorWebAssembly/_Imports.razor index 07e96c3..baf3f18 100644 --- a/samples/BlazorWebAssembly/_Imports.razor +++ b/samples/BlazorWebAssembly/_Imports.razor @@ -1,7 +1,10 @@ @using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web -@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop @using BlazorWebAssembly @using BlazorWebAssembly.Shared diff --git a/samples/BlazorWebAssembly/wwwroot/css/app.css b/samples/BlazorWebAssembly/wwwroot/css/app.css new file mode 100644 index 0000000..82fc22a --- /dev/null +++ b/samples/BlazorWebAssembly/wwwroot/css/app.css @@ -0,0 +1,50 @@ +@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); + +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +a, .btn-link { + color: #0366d6; +} + +.btn-primary { + color: #fff; + background-color: #1b6ec2; + border-color: #1861ac; +} + +.content { + padding-top: 1.1rem; +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid red; +} + +.validation-message { + color: red; +} + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/samples/BlazorWebAssembly/wwwroot/css/site.css b/samples/BlazorWebAssembly/wwwroot/css/site.css deleted file mode 100644 index 7338534..0000000 --- a/samples/BlazorWebAssembly/wwwroot/css/site.css +++ /dev/null @@ -1,123 +0,0 @@ -@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); - -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -app { - position: relative; - display: flex; - flex-direction: column; -} - -.top-row { - height: 3.5rem; - display: flex; - align-items: center; -} - -.main { - flex: 1; -} - - .main .top-row { - background-color: #e6e6e6; - border-bottom: 1px solid #d6d5d5; - } - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - - .sidebar .top-row { - background-color: rgba(0,0,0,0.4); - } - - .sidebar .navbar-brand { - font-size: 1.1rem; - } - - .sidebar .oi { - width: 2rem; - font-size: 1.1rem; - vertical-align: text-top; - top: -2px; - } - -.nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; -} - - .nav-item:first-of-type { - padding-top: 1rem; - } - - .nav-item:last-of-type { - padding-bottom: 1rem; - } - - .nav-item a { - color: #d7d7d7; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - } - - .nav-item a.active { - background-color: rgba(255,255,255,0.25); - color: white; - } - - .nav-item a:hover { - background-color: rgba(255,255,255,0.1); - color: white; - } - -.content { - padding-top: 1.1rem; -} - -.navbar-toggler { - background-color: rgba(255, 255, 255, 0.1); -} - -@media (max-width: 767.98px) { - .main .top-row { - display: none; - } -} - -@media (min-width: 768px) { - app { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .main .top-row { - position: sticky; - top: 0; - } - - .main > div { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } - - .navbar-toggler { - display: none; - } - - .sidebar .collapse { - /* Never collapse the sidebar for wide screens */ - display: block; - } -} diff --git a/samples/BlazorWebAssembly/wwwroot/index.html b/samples/BlazorWebAssembly/wwwroot/index.html index bc8f750..980817a 100644 --- a/samples/BlazorWebAssembly/wwwroot/index.html +++ b/samples/BlazorWebAssembly/wwwroot/index.html @@ -1,13 +1,14 @@  - - Blazored SessionStorage + Blazored SessionStorage (Wasm) + - + + - - Loading... +
Loading...
An unhandled error has occurred. @@ -75,5 +75,4 @@ }); - - \ No newline at end of file + diff --git a/samples/bUnitExample/Counter.razor b/samples/bUnitExample/Counter.razor new file mode 100644 index 0000000..cb6f9e1 --- /dev/null +++ b/samples/bUnitExample/Counter.razor @@ -0,0 +1,16 @@ +

Counter

+ +

+ Current count: @currentCount +

+ + + +@code { + int currentCount = 0; + + void IncrementCount() + { + currentCount++; + } +} diff --git a/samples/bUnitExample/IndexPageTests.cs b/samples/bUnitExample/IndexPageTests.cs new file mode 100644 index 0000000..31c1492 --- /dev/null +++ b/samples/bUnitExample/IndexPageTests.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using Bunit; +using Xunit; + +namespace bUnitExample +{ + public class IndexPageTests : TestContext + { + [Fact] + public async Task SavesNameToSessionStorage() + { + // Arrange + const string inputName = "John Smith"; + var sessionStorage = this.AddBlazoredSessionStorage(); + var cut = RenderComponent(); + + // Act + cut.Find("#Name").Change(inputName); + cut.Find("#NameButton").Click(); + + // Assert + var name = await sessionStorage.GetItemAsync("name"); + + Assert.Equal(inputName, name); + } + } +} diff --git a/samples/bUnitExample/_Imports.razor b/samples/bUnitExample/_Imports.razor new file mode 100644 index 0000000..5703c20 --- /dev/null +++ b/samples/bUnitExample/_Imports.razor @@ -0,0 +1,6 @@ +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop +@using Microsoft.Extensions.DependencyInjection +@using Bunit +@using Bunit.TestDoubles +@using Xunit diff --git a/samples/bUnitExample/bUnitExample.csproj b/samples/bUnitExample/bUnitExample.csproj new file mode 100644 index 0000000..6d5685c --- /dev/null +++ b/samples/bUnitExample/bUnitExample.csproj @@ -0,0 +1,26 @@ + + + + net5.0 + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + \ No newline at end of file diff --git a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj index 3226b58..8191ad3 100644 --- a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj +++ b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj @@ -1,20 +1,18 @@  - netstandard2.0 - 3.0 + netstandard2.1;net5.0 Chris Sainty - Copyright 2020 (c) Chris Sainty. All rights reserved. - + Copyright 2020 (c) Chris Sainty. All rights reserved. A library to provide access to session storage in Blazor applications true snupkg Blazored.SessionStorage - Blazored;Blazor;Razor;Components;Session Storage;Browser Storage;ASP.NET Core;CSharp;Web + Blazored;Blazor;Razor;Components;SessionStorage;Session Storage;ASP.NET Core;CSharp;Web MIT https://github.com/Blazored/SessionStorage icon.png @@ -24,15 +22,34 @@ true true + true + Blazored.SessionStorage.snk - - - + + 3.0 + + + + + + + + + + + + + + - + + + + + diff --git a/src/Blazored.SessionStorage/Blazored.SessionStorage.snk b/src/Blazored.SessionStorage/Blazored.SessionStorage.snk new file mode 100644 index 0000000000000000000000000000000000000000..18e2ce3056b7ac1a4950e9cd2874f423e197e0d5 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097XC*dN@Hrx}&m8A4Ld@ZB)F|O2B?J$u* zz||sRGCn2POYeW}+>6qj{zmL&A%@Ek;92h4zc26rxp$&BYGFOh`w1=P!b9MQjkLyU z2tg#W!Fik7OS>;{{?Irt!OPnEZNT)p%-k&QRU=P|TGwVp!xd6L?o(3P@DMiSixcDb zt^>MkhmWu1(SrN*jcP{D4WaB3D-(A8AKI~^zhV#M#A~w*7qq&wAOA6UeNlJhxKgAu zI8cBn8@Q-Z#xCpg#j=cSld0UJAY7goaqnPnr!=w^e{qWGCOwx&HfbyKeTdH$p|_yH zN-#qZauNGh)Nh`fyON*fg0*M}6)pFHu!((3ShrkRuHQP;K55xA`PX@pXHwQi0nj+i zIT;V@@*i`vm(K~7Eg$Tyq)NpfY_d&$KO3WWIW>+77?wD zJMvLM69^UTy``CbNS>DAT}1Oc$H7PDHz4CWiVre`iH8-F1^Q!gb=b%UTovXOt4ws` zq4dX5E5ZohBuXYBMNT33s|$?;3OblFAQgz+LgMM|1Y>$S{LVUk;(}2ZXjdSiUjch3 zTiH!n<98=1rZr`~17ue9?<0Nf|7=Y$fp5av;aWUYr)CX3o1aJG)4pDcE%R@g4vL~L z@2Ej)ZtUCuI+i)bH&4N1TfMxYE _jSRuntime.InvokeVoidAsync("sessionStorage.clear"); + + public ValueTask GetItemAsync(string key) + => _jSRuntime.InvokeAsync("sessionStorage.getItem", key); + + public ValueTask KeyAsync(int index) + => _jSRuntime.InvokeAsync("sessionStorage.key", index); + + public ValueTask ContainKeyAsync(string key) + => _jSRuntime.InvokeAsync("sessionStorage.hasOwnProperty", key); + + public ValueTask LengthAsync() + => _jSRuntime.InvokeAsync("eval", "sessionStorage.length"); + + public ValueTask RemoveItemAsync(string key) + => _jSRuntime.InvokeVoidAsync("sessionStorage.removeItem", key); + + public ValueTask SetItemAsync(string key, string data) + => _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", key, data); + + public void Clear() + { + CheckForInProcessRuntime(); + _jSInProcessRuntime.InvokeVoid("sessionStorage.clear"); + } + + public string GetItem(string key) + { + CheckForInProcessRuntime(); + return _jSInProcessRuntime.Invoke("sessionStorage.getItem", key); + } + + public string Key(int index) + { + CheckForInProcessRuntime(); + return _jSInProcessRuntime.Invoke("sessionStorage.key", index); + } + + public bool ContainKey(string key) + { + CheckForInProcessRuntime(); + return _jSInProcessRuntime.Invoke("sessionStorage.hasOwnProperty", key); + } + + public int Length() + { + CheckForInProcessRuntime(); + return _jSInProcessRuntime.Invoke("eval", "sessionStorage.length"); + } + + public void RemoveItem(string key) + { + CheckForInProcessRuntime(); + _jSInProcessRuntime.InvokeVoidAsync("sessionStorage.removeItem", key); + } + + public void SetItem(string key, string data) + { + CheckForInProcessRuntime(); + _jSInProcessRuntime.InvokeVoid("sessionStorage.setItem", key, data); + } + + private void CheckForInProcessRuntime() + { + if (_jSInProcessRuntime == null) + throw new InvalidOperationException("IJSInProcessRuntime not available"); + } + } +} diff --git a/src/Blazored.SessionStorage/ChangedEventArgs.cs b/src/Blazored.SessionStorage/ChangedEventArgs.cs index 818909c..fae2781 100644 --- a/src/Blazored.SessionStorage/ChangedEventArgs.cs +++ b/src/Blazored.SessionStorage/ChangedEventArgs.cs @@ -1,5 +1,8 @@ -namespace Blazored.SessionStorage +using System.Diagnostics.CodeAnalysis; + +namespace Blazored.SessionStorage { + [ExcludeFromCodeCoverage] public class ChangedEventArgs { public string Key { get; set; } diff --git a/src/Blazored.SessionStorage/ChangingEventArgs.cs b/src/Blazored.SessionStorage/ChangingEventArgs.cs index 2a38e76..047a3e3 100644 --- a/src/Blazored.SessionStorage/ChangingEventArgs.cs +++ b/src/Blazored.SessionStorage/ChangingEventArgs.cs @@ -1,7 +1,9 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Blazored.SessionStorage { + [ExcludeFromCodeCoverage] public class ChangingEventArgs : ChangedEventArgs { public bool Cancel { get; set; } diff --git a/src/Blazored.SessionStorage/ISessionStorageService.cs b/src/Blazored.SessionStorage/ISessionStorageService.cs index 0be8e7e..476435f 100644 --- a/src/Blazored.SessionStorage/ISessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISessionStorageService.cs @@ -5,27 +5,62 @@ namespace Blazored.SessionStorage { public interface ISessionStorageService { - Task ClearAsync(); + /// + /// Clears all data from session storage. + /// + /// A representing the completion of the operation. + ValueTask ClearAsync(); + + /// + /// Retrieve the specified data from session storage and deseralise it to the specfied type. + /// + /// A value specifying the name of the session storage slot to use + /// A representing the completion of the operation. + ValueTask GetItemAsync(string key); - Task GetItemAsync(string key); + /// + /// Retrieve the specified data from session storage as a . + /// + /// A value specifying the name of the storage slot to use + /// A representing the completion of the operation. + ValueTask GetItemAsStringAsync(string key); - Task KeyAsync(int index); + /// + /// Return the name of the key at the specified . + /// + /// + /// A representing the completion of the operation. + ValueTask KeyAsync(int index); /// - /// Checks if the key exists in Session Storage but does not check the value. + /// Checks if the exists in session storage, but does not check its value. /// - /// name of the key - /// True if the key exist, false otherwise - Task ContainKeyAsync(string key); + /// A value specifying the name of the storage slot to use + /// A representing the completion of the operation. + ValueTask ContainKeyAsync(string key); - Task LengthAsync(); + /// + /// The number of items stored in session storage. + /// + /// A representing the completion of the operation. + ValueTask LengthAsync(); - Task RemoveItemAsync(string key); + /// + /// Remove the data with the specified . + /// + /// A value specifying the name of the storage slot to use + /// A representing the completion of the operation. + ValueTask RemoveItemAsync(string key); - Task SetItemAsync(string key, T data); + /// + /// Sets or updates the in session storage with the specified . + /// + /// A value specifying the name of the storage slot to use + /// The data to be saved + /// A representing the completion of the operation. + ValueTask SetItemAsync(string key, T data); event EventHandler Changing; - event EventHandler Changed; } -} \ No newline at end of file +} diff --git a/src/Blazored.SessionStorage/IStorageProvider.cs b/src/Blazored.SessionStorage/IStorageProvider.cs new file mode 100644 index 0000000..a45f204 --- /dev/null +++ b/src/Blazored.SessionStorage/IStorageProvider.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +namespace Blazored.SessionStorage +{ + internal interface IStorageProvider + { + void Clear(); + ValueTask ClearAsync(); + bool ContainKey(string key); + ValueTask ContainKeyAsync(string key); + string GetItem(string key); + ValueTask GetItemAsync(string key); + string Key(int index); + ValueTask KeyAsync(int index); + int Length(); + ValueTask LengthAsync(); + void RemoveItem(string key); + ValueTask RemoveItemAsync(string key); + void SetItem(string key, string data); + ValueTask SetItemAsync(string key, string data); + } +} \ No newline at end of file diff --git a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs index 1bdad3b..f1efb32 100644 --- a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs @@ -4,27 +4,59 @@ namespace Blazored.SessionStorage { public interface ISyncSessionStorageService { + /// + /// Clears all data from session storage. + /// void Clear(); + /// + /// Retrieve the specified data from session storage as a . + /// + /// A value specifying the name of the session storage slot to use + /// The data from the specified as a T GetItem(string key); + /// + /// Retrieve the specified data from session storage as a . + /// + /// A value specifying the name of the storage slot to use + /// The data associated with the specified as a + string GetItemAsString(string key); + + /// + /// Return the name of the key at the specified . + /// + /// + /// The name of the key at the specified string Key(int index); /// - /// Checks if the key exists in Session Storage but does not check the value. + /// Checks if the exists in session storage, but does not check its value. /// - /// name of the key - /// True if the key exist, false otherwise + /// A value specifying the name of the storage slot to use + /// Boolean indicating if the specified exists bool ContainKey(string key); + /// + /// The number of items stored in session storage. + /// + /// The number of items stored in session storage int Length(); + /// + /// Remove the data with the specified . + /// + /// A value specifying the name of the storage slot to use void RemoveItem(string key); + /// + /// Sets or updates the in session storage with the specified . + /// + /// A value specifying the name of the storage slot to use + /// The data to be saved void SetItem(string key, T data); event EventHandler Changing; - event EventHandler Changed; } -} \ No newline at end of file +} diff --git a/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs b/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs index f2436e3..b67e0a8 100644 --- a/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs +++ b/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; @@ -23,18 +25,13 @@ public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, Jso { return TimeSpan.Zero; } - else + + if (!TimeSpan.TryParseExact(s, TimeSpanFormatString, null, out var parsedTimeSpan)) { - TimeSpan parsedTimeSpan; - if (!TimeSpan.TryParseExact(s, TimeSpanFormatString, null, out parsedTimeSpan)) - { - throw new FormatException($"Input timespan is not in an expected format : expected {Regex.Unescape(TimeSpanFormatString)}. Please retrieve this key as a string and parse manually."); - } - else - { - return parsedTimeSpan; - } + throw new FormatException($"Input timespan is not in an expected format : expected {Regex.Unescape(TimeSpanFormatString)}. Please retrieve this key as a string and parse manually."); } + + return parsedTimeSpan; } public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) diff --git a/src/Blazored.SessionStorage/PublicKey.snk b/src/Blazored.SessionStorage/PublicKey.snk new file mode 100644 index 0000000000000000000000000000000000000000..031abef3d62315bb6368ee49f2f9f59da8f6df16 GIT binary patch literal 160 zcmV;R0AK$ABme*efB*oL000060ssI2Bme+XQ$aBR1ONa50097XC*dN@Hrx}&m8A4L zd@ZB)F|O2B?J$u*z||sRGCn2POYeW}+>6qj{zmL&A%@Ek;92h4zc26rxp$&BYGFOh z`w1=P!b9MQjkLyU2tg#W!Fik7OS>;{{?Irt!OPnEZNT)p%-k&QRU=P|TGwVp!xd6L O?o(3P@DMiSixcC{1xJVg literal 0 HcmV?d00001 diff --git a/src/Blazored.SessionStorage/PublicKey.txt b/src/Blazored.SessionStorage/PublicKey.txt new file mode 100644 index 0000000..0a9702e --- /dev/null +++ b/src/Blazored.SessionStorage/PublicKey.txt @@ -0,0 +1,5 @@ + +Microsoft (R) .NET Framework Strong Name Utility Version 4.0.30319.0 +Copyright (c) Microsoft Corporation. All rights reserved. + +Public key token is 6f8d36f00e4891c3 diff --git a/src/Blazored.SessionStorage/Serialization/IJsonSerializer.cs b/src/Blazored.SessionStorage/Serialization/IJsonSerializer.cs new file mode 100644 index 0000000..9bccf18 --- /dev/null +++ b/src/Blazored.SessionStorage/Serialization/IJsonSerializer.cs @@ -0,0 +1,8 @@ +namespace Blazored.SessionStorage.Serialization +{ + public interface IJsonSerializer + { + string Serialize(T obj); + T Deserialize(string text); + } +} diff --git a/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs b/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs new file mode 100644 index 0000000..8100bab --- /dev/null +++ b/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs @@ -0,0 +1,29 @@ +using Blazored.SessionStorage.StorageOptions; +using Microsoft.Extensions.Options; +using System.Runtime.CompilerServices; +using System.Text.Json; + +[assembly: InternalsVisibleTo("Blazored.SessionStorage.Tests, PublicKey=" + +"00240000048000009400000006020000002400005253413100040000010001007527e122cc36dc" + +"13c695a4f43b7c2da3f631aed456ed309140c0d52262323e25d84bef7feddc8bd29cfe46ec6521" + +"86cb10e059eedabf2ff000b977a2376a613dccfb092de6c243e0888db4c66a084124b2c1799bda" + +"4bbb2f70fed0382fc1cbdafa6dc0f4baccdc2cee55234f8a5ad76645c315523fee5352d9f01036" + +"e48b13e3")] +namespace Blazored.SessionStorage.Serialization +{ + internal class SystemTextJsonSerializer : IJsonSerializer + { + private readonly JsonSerializerOptions _options; + + public SystemTextJsonSerializer(IOptions options) + { + _options = options.Value.JsonSerializerOptions; + } + + public T Deserialize(string data) + => JsonSerializer.Deserialize(data, _options); + + public string Serialize(T data) + => JsonSerializer.Serialize(data, _options); + } +} diff --git a/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs b/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs index 4bb05e7..9b1e2bf 100644 --- a/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs +++ b/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs @@ -1,33 +1,30 @@ using System; +using System.Diagnostics.CodeAnalysis; using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Microsoft.Extensions.DependencyInjection; - -namespace Blazored.SessionStorage -{ - public static class ServiceCollectionExtensions - { - public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services) - { - return services - .AddScoped() - .AddScoped() - .Configure(configureOptions => - { - configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); - }); - } +using Microsoft.Extensions.DependencyInjection; + +namespace Blazored.SessionStorage +{ + [ExcludeFromCodeCoverage] + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services) + => AddBlazoredSessionStorage(services, null); public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services, Action configure) { return services - .AddScoped() - .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() .Configure(configureOptions => { configure?.Invoke(configureOptions); configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); }); } - } -} + } +} diff --git a/src/Blazored.SessionStorage/SessionStorageService.cs b/src/Blazored.SessionStorage/SessionStorageService.cs index fbbe878..8bdd847 100644 --- a/src/Blazored.SessionStorage/SessionStorageService.cs +++ b/src/Blazored.SessionStorage/SessionStorageService.cs @@ -1,160 +1,160 @@ -using Blazored.SessionStorage.StorageOptions; -using Microsoft.Extensions.Options; -using Microsoft.JSInterop; -using System; +using System; using System.Text.Json; using System.Threading.Tasks; +using Blazored.SessionStorage.Serialization; namespace Blazored.SessionStorage { - public class SessionStorageService : ISessionStorageService, ISyncSessionStorageService + internal class SessionStorageService : ISessionStorageService, ISyncSessionStorageService { - private readonly IJSRuntime _jSRuntime; - private readonly IJSInProcessRuntime _jSInProcessRuntime; - private readonly JsonSerializerOptions _jsonOptions; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; - public event EventHandler Changing; - - public event EventHandler Changed; - - public SessionStorageService(IJSRuntime jSRuntime, IOptions options) + public SessionStorageService(IStorageProvider storageProvider, IJsonSerializer serializer) { - _jSRuntime = jSRuntime; - _jsonOptions = options.Value.JsonSerializerOptions; - _jSInProcessRuntime = jSRuntime as IJSInProcessRuntime; + _storageProvider = storageProvider; + _serializer = serializer; } - public async Task SetItemAsync(string key, T data) + public async ValueTask SetItemAsync(string key, T data) { - if (string.IsNullOrEmpty(key)) + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - var e = await RaiseOnChangingAsync(key, data); + var e = await RaiseOnChangingAsync(key, data).ConfigureAwait(false); if (e.Cancel) return; - var serialisedData = JsonSerializer.Serialize(data, _jsonOptions); - - await _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", key, serialisedData); + var serialisedData = _serializer.Serialize(data); + await _storageProvider.SetItemAsync(key, serialisedData).ConfigureAwait(false); RaiseOnChanged(key, e.OldValue, data); } - public async Task GetItemAsync(string key) + public async ValueTask GetItemAsync(string key) { - if (string.IsNullOrEmpty(key)) + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - var serialisedData = await _jSRuntime.InvokeAsync("sessionStorage.getItem", key); + var serialisedData = await _storageProvider.GetItemAsync(key).ConfigureAwait(false); - if (serialisedData == null) + if (string.IsNullOrWhiteSpace(serialisedData)) return default; - return JsonSerializer.Deserialize(serialisedData, _jsonOptions); + try + { + return _serializer.Deserialize(serialisedData); + } + catch (JsonException e) when (e.Path == "$" && typeof(T) == typeof(string)) + { + // For backward compatibility return the plain string. + // On the next save a correct value will be stored and this Exception will not happen again, for this 'key' + return (T)(object)serialisedData; + } + } + + public ValueTask GetItemAsStringAsync(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); + + return _storageProvider.GetItemAsync(key); } - public async Task RemoveItemAsync(string key) + public ValueTask RemoveItemAsync(string key) { - if (string.IsNullOrEmpty(key)) + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - await _jSRuntime.InvokeAsync("sessionStorage.removeItem", key); + return _storageProvider.RemoveItemAsync(key); } - public async Task ClearAsync() => await _jSRuntime.InvokeAsync("sessionStorage.clear"); + public ValueTask ClearAsync() + => _storageProvider.ClearAsync(); - public async Task LengthAsync() => await _jSRuntime.InvokeAsync("eval", "sessionStorage.length"); + public ValueTask LengthAsync() + => _storageProvider.LengthAsync(); - public async Task KeyAsync(int index) => await _jSRuntime.InvokeAsync("sessionStorage.key", index); + public ValueTask KeyAsync(int index) + => _storageProvider.KeyAsync(index); - public async Task ContainKeyAsync(string key) => await _jSRuntime.InvokeAsync("sessionStorage.hasOwnProperty", key); + public ValueTask ContainKeyAsync(string key) + => _storageProvider.ContainKeyAsync(key); public void SetItem(string key, T data) { - if (string.IsNullOrEmpty(key)) + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); - var e = RaiseOnChangingSync(key, data); if (e.Cancel) return; - var serialisedData = JsonSerializer.Serialize(data, _jsonOptions); - - _jSInProcessRuntime.InvokeVoid("sessionStorage.setItem", key, serialisedData); + var serialisedData = _serializer.Serialize(data); + _storageProvider.SetItem(key, serialisedData); RaiseOnChanged(key, e.OldValue, data); } public T GetItem(string key) { - if (string.IsNullOrEmpty(key)) + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); - - var serialisedData = _jSInProcessRuntime.Invoke("sessionStorage.getItem", key); + var serialisedData = _storageProvider.GetItem(key); - if (serialisedData == null) + if (string.IsNullOrWhiteSpace(serialisedData)) return default; - return JsonSerializer.Deserialize(serialisedData, _jsonOptions); + try + { + return _serializer.Deserialize(serialisedData); + } + catch (JsonException e) when (e.Path == "$" && typeof(T) == typeof(string)) + { + // For backward compatibility return the plain string. + // On the next save a correct value will be stored and this Exception will not happen again, for this 'key' + return (T)(object)serialisedData; + } } - public void RemoveItem(string key) + public string GetItemAsString(string key) { - if (string.IsNullOrEmpty(key)) + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); - - _jSInProcessRuntime.InvokeVoid("sessionStorage.removeItem", key); + return _storageProvider.GetItem(key); } - public void Clear() + public void RemoveItem(string key) { - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - _jSInProcessRuntime.InvokeVoid("sessionStorage.clear"); + _storageProvider.RemoveItem(key); } - public int Length() - { - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); + public void Clear() + => _storageProvider.Clear(); - return _jSInProcessRuntime.Invoke("eval", "sessionStorage.length"); - } + public int Length() + => _storageProvider.Length(); public string Key(int index) - { - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); - - return _jSInProcessRuntime.Invoke("sessionStorage.key", index); - } + => _storageProvider.Key(index); public bool ContainKey(string key) - { - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); - - return _jSInProcessRuntime.Invoke("sessionStorage.hasOwnProperty", key); - } + => _storageProvider.ContainKey(key); + public event EventHandler Changing; private async Task RaiseOnChangingAsync(string key, object data) { var e = new ChangingEventArgs { Key = key, - OldValue = await GetItemAsync(key), + OldValue = await GetItemInternalAsync(key).ConfigureAwait(false), NewValue = data }; @@ -168,7 +168,7 @@ private ChangingEventArgs RaiseOnChangingSync(string key, object data) var e = new ChangingEventArgs { Key = key, - OldValue = ((ISyncSessionStorageService)this).GetItem(key), + OldValue = GetItemInternal(key), NewValue = data }; @@ -177,6 +177,46 @@ private ChangingEventArgs RaiseOnChangingSync(string key, object data) return e; } + private async Task GetItemInternalAsync(string key) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); + + var serialisedData = await _storageProvider.GetItemAsync(key).ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(serialisedData)) + return default; + try + { + return _serializer.Deserialize(serialisedData); + } + catch (JsonException) + { + return (T)(object)serialisedData; + } + } + + private object GetItemInternal(string key) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); + + var serialisedData = _storageProvider.GetItem(key); + + if (string.IsNullOrWhiteSpace(serialisedData)) + return default; + + try + { + return _serializer.Deserialize(serialisedData); + } + catch (JsonException) + { + return serialisedData; + } + } + + public event EventHandler Changed; private void RaiseOnChanged(string key, object oldValue, object data) { var e = new ChangedEventArgs @@ -189,4 +229,4 @@ private void RaiseOnChanged(string key, object oldValue, object data) Changed?.Invoke(this, e); } } -} \ No newline at end of file +} diff --git a/src/Blazored.SessionStorage/StorageOptions/LocalStorageOptions.cs b/src/Blazored.SessionStorage/StorageOptions/LocalStorageOptions.cs new file mode 100644 index 0000000..509f1f0 --- /dev/null +++ b/src/Blazored.SessionStorage/StorageOptions/LocalStorageOptions.cs @@ -0,0 +1,9 @@ +using System.Text.Json; + +namespace Blazored.SessionStorage.StorageOptions +{ + public class SessionStorageOptions + { + public JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions(); + } +} diff --git a/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs b/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs deleted file mode 100644 index b005f1a..0000000 --- a/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json; - -namespace Blazored.SessionStorage.StorageOptions -{ - public class SessionStorageOptions - { - public JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions - { - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - IgnoreNullValues = true, - IgnoreReadOnlyProperties = true, - PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Skip, - WriteIndented = false - }; - } -} diff --git a/src/Blazored.SessionStorage/Testing/BUnitLocalStorageTestExtensions.cs b/src/Blazored.SessionStorage/Testing/BUnitLocalStorageTestExtensions.cs new file mode 100644 index 0000000..efaadcf --- /dev/null +++ b/src/Blazored.SessionStorage/Testing/BUnitLocalStorageTestExtensions.cs @@ -0,0 +1,37 @@ +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Diagnostics.CodeAnalysis; +using Blazored.SessionStorage; +using Blazored.SessionStorage.Testing; + +namespace Bunit +{ + [ExcludeFromCodeCoverage] + public static class BUnitSessionStorageTestExtensions + { + public static ISessionStorageService AddBlazoredSessionStorage(this TestContextBase context) + => AddBlazoredSessionStorage(context, null); + + public static ISessionStorageService AddBlazoredSessionStorage(this TestContextBase context, Action configure) + { + if (context is null) + throw new ArgumentNullException(nameof(context)); + + context.Services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .Configure(configureOptions => + { + configure?.Invoke(configureOptions); + configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); + }); + + return context.Services.GetService(); + } + } +} diff --git a/src/Blazored.SessionStorage/Testing/InMemoryStorageProvider.cs b/src/Blazored.SessionStorage/Testing/InMemoryStorageProvider.cs new file mode 100644 index 0000000..1298766 --- /dev/null +++ b/src/Blazored.SessionStorage/Testing/InMemoryStorageProvider.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blazored.SessionStorage.Testing +{ + internal class InMemoryStorageProvider : IStorageProvider + { + private readonly Dictionary _dataStore = new Dictionary(); + + public void Clear() + => _dataStore.Clear(); + + public ValueTask ClearAsync() + { + _dataStore.Clear(); + return new ValueTask(Task.CompletedTask); + } + + public bool ContainKey(string key) + => _dataStore.ContainsKey(key); + + public ValueTask ContainKeyAsync(string key) + => new ValueTask(ContainKey(key)); + + public string GetItem(string key) + => _dataStore.ContainsKey(key) ? _dataStore[key] : default; + + public ValueTask GetItemAsync(string key) + => new ValueTask(GetItem(key)); + + public string Key(int index) + => index > _dataStore.Count - 1 ? default : _dataStore.ElementAt(index).Key; + + public ValueTask KeyAsync(int index) + => new ValueTask(Key(index)); + + public int Length() + => _dataStore.Count; + + public ValueTask LengthAsync() + => new ValueTask(Length()); + + public void RemoveItem(string key) + => _dataStore.Remove(key); + + public ValueTask RemoveItemAsync(string key) + { + RemoveItem(key); + return new ValueTask(Task.CompletedTask); + } + + public void SetItem(string key, string data) + { + if (_dataStore.ContainsKey(key)) + { + _dataStore[key] = data; + } + else + { + _dataStore.Add(key, data); + } + } + + public ValueTask SetItemAsync(string key, string data) + { + SetItem(key, data); + return new ValueTask(Task.CompletedTask); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj b/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj new file mode 100644 index 0000000..01c5fd1 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj @@ -0,0 +1,39 @@ + + + + net5.0 + + false + + ..\..\src\Blazored.SessionStorage\Blazored.SessionStorage.snk + + true + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + Blazored.SessionStorage.snk + + + + diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs new file mode 100644 index 0000000..c29f9f3 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs @@ -0,0 +1,58 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class Clear + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + + public Clear() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } + + [Fact] + public void ClearsAnyItemsInTheStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + _sut.SetItem("Item1", item1); + _sut.SetItem("Item2", item2); + + // Act + _sut.Clear(); + + // Assert + Assert.Equal(0, _storageProvider.Length()); + } + + [Fact] + public void DoesNothingWhenStoreIsEmpty() + { + // Act + _sut.Clear(); + + // Assert + Assert.Equal(0, _storageProvider.Length()); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs new file mode 100644 index 0000000..159894c --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs @@ -0,0 +1,58 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class ClearAsync + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + + public ClearAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } + + [Fact] + public async Task ClearsAnyItemsInTheStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + await _sut.SetItemAsync("Item1", item1); + await _sut.SetItemAsync("Item2", item2); + + // Act + await _sut.ClearAsync(); + + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } + + [Fact] + public async Task DoesNothingWhenItemDoesNotExistInStore() + { + // Act + await _sut.ClearAsync(); + + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs new file mode 100644 index 0000000..d56f77a --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs @@ -0,0 +1,58 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class ContainsKey + { + private readonly SessionStorageService _sut; + + public ContainsKey() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public void ReturnsTrue_When_KeyExistsInStore() + { + // Arrange + const string key = "TestKey"; + var item1 = new TestObject(1, "Jane Smith"); + _sut.SetItem(key, item1); + + // Act + var containsKey = _sut.ContainKey(key); + + // Assert + Assert.True(containsKey); + } + + [Fact] + public void ReturnsFalse_When_KeyDoesNotExistsInStore() + { + // Arrange + const string key = "TestKey"; + + // Act + var containsKey = _sut.ContainKey(key); + + // Assert + Assert.False(containsKey); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs new file mode 100644 index 0000000..9fb4474 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs @@ -0,0 +1,58 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class ContainsKeyAsync + { + private readonly SessionStorageService _sut; + + public ContainsKeyAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public async Task ReturnsTrue_When_KeyExistsInStore() + { + // Arrange + const string key = "TestKey"; + var item1 = new TestObject(1, "Jane Smith"); + await _sut.SetItemAsync(key, item1); + + // Act + var containsKey = await _sut.ContainKeyAsync(key); + + // Assert + Assert.True(containsKey); + } + + [Fact] + public async Task ReturnsFalse_When_KeyDoesNotExistsInStore() + { + // Arrange + const string key = "TestKey"; + + // Act + var containsKey = await _sut.ContainKeyAsync(key); + + // Assert + Assert.False(containsKey); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs new file mode 100644 index 0000000..850c041 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs @@ -0,0 +1,106 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class GetItem + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public GetItem() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Action(() => _sut.GetItem(key)); + + // assert + Assert.Throws(action); + } + + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public void ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + _sut.SetItem(key, data); + + // Act + var result = _sut.GetItem(key); + + // Assert + Assert.Equal(data, result); + } + + [Fact] + public void ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + _sut.SetItem(Key, objectToSave); + + // Act + var result = _sut.GetItem(Key); + + // Assert + Assert.Equal(objectToSave.Id, result.Id); + Assert.Equal(objectToSave.Name, result.Name); + } + + [Fact] + public void ReturnsNullFromStore_When_NullValueSaved() + { + // Arrange + var valueToSave = (string)null; + _sut.SetItem(Key, valueToSave); + + // Act + var result = _sut.GetItem(Key); + + // Assert + Assert.Null(result); + } + + [Fact] + public void ReturnsStringFromStore_When_JsonExceptionIsThrown() + { + // Arrange + var jsonData = "[{ id: 5, name: \"Jane Smith\"}]"; + _storageProvider.SetItem(Key, jsonData); + + // Act + var result = _sut.GetItem(Key); + + // Assert + Assert.Equal(jsonData, result); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs new file mode 100644 index 0000000..b3efe67 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs @@ -0,0 +1,79 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class GetItemAsString + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public GetItemAsString() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Action(() => _sut.GetItemAsString(key)); + + // assert + Assert.Throws(action); + } + + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public void ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + _sut.SetItem(key, data); + + // Act + var result = _sut.GetItemAsString(key); + + // Assert + var serializedData = _serializer.Serialize(data); + Assert.Equal(serializedData, result); + } + + [Fact] + public void ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + _sut.SetItem(Key, objectToSave); + + // Act + var result = _sut.GetItemAsString(Key); + + // Assert + var serializedData = _serializer.Serialize(objectToSave); + Assert.Equal(serializedData, result); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs new file mode 100644 index 0000000..c024edc --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs @@ -0,0 +1,79 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class GetItemAsStringAsync + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public GetItemAsStringAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Func(async () => await _sut.GetItemAsStringAsync(key)); + + // assert + Assert.ThrowsAsync(action); + } + + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public async Task ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + await _sut.SetItemAsync(key, data); + + // Act + var result = await _sut.GetItemAsStringAsync(key); + + // Assert + var serializedData = _serializer.Serialize(data); + Assert.Equal(serializedData, result); + } + + [Fact] + public async Task ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + await _sut.SetItemAsync(Key, objectToSave); + + // Act + var result = await _sut.GetItemAsStringAsync(Key); + + // Assert + var serializedData = _serializer.Serialize(objectToSave); + Assert.Equal(serializedData, result); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs new file mode 100644 index 0000000..73bbe87 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs @@ -0,0 +1,106 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class GetItemAsync + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public GetItemAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Func(async () => await _sut.GetItemAsync(key)); + + // assert + Assert.ThrowsAsync(action); + } + + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public async Task ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + await _sut.SetItemAsync(key, data); + + // Act + var result = await _sut.GetItemAsync(key); + + // Assert + Assert.Equal(data, result); + } + + [Fact] + public async Task ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + await _sut.SetItemAsync(Key, objectToSave); + + // Act + var result = await _sut.GetItemAsync(Key); + + // Assert + Assert.Equal(objectToSave.Id, result.Id); + Assert.Equal(objectToSave.Name, result.Name); + } + + [Fact] + public async Task ReturnsNullFromStore_When_NullValueSaved() + { + // Arrange + var valueToSave = (string)null; + await _sut.SetItemAsync(Key, valueToSave); + + // Act + var result = await _sut.GetItemAsync(Key); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task ReturnsStringFromStore_When_JsonExceptionIsThrown() + { + // Arrange + var valueToSave = "[{ id: 5, name: \"Jane Smith\"}]"; + await _storageProvider.SetItemAsync(Key, valueToSave); + + // Act + var result = await _sut.GetItemAsync(Key); + + // Assert + Assert.Equal(valueToSave, result); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs new file mode 100644 index 0000000..5413869 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs @@ -0,0 +1,64 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class Key + { + private readonly SessionStorageService _sut; + + public Key() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public void ReturnsNameOfKeyAtIndex_When_KeyExistsInStore() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; + + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + _sut.SetItem(key1, item1); + _sut.SetItem(key2, item2); + + // Act + var keyName = _sut.Key(1); + + // Assert + Assert.Equal(key2, keyName); + } + + [Fact] + public void ReturnsNull_When_KeyDoesNotExistInStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + _sut.SetItem("TestKey1", item1); + + // Act + var keyName = _sut.Key(1); + + // Assert + Assert.Null(keyName); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs new file mode 100644 index 0000000..b1ca855 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs @@ -0,0 +1,64 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class KeyAsync + { + private readonly SessionStorageService _sut; + + public KeyAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public async Task ReturnsNameOfKeyAtIndex_When_KeyExistsInStore() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; + + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + await _sut.SetItemAsync(key1, item1); + await _sut.SetItemAsync(key2, item2); + + // Act + var keyName = await _sut.KeyAsync(1); + + // Assert + Assert.Equal(key2, keyName); + } + + [Fact] + public async Task ReturnsNull_When_KeyDoesNotExistInStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + await _sut.SetItemAsync("TestKey1", item1); + + // Act + var keyName = await _sut.KeyAsync(1); + + // Assert + Assert.Null(keyName); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs new file mode 100644 index 0000000..dbb4664 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs @@ -0,0 +1,57 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class Length + { + private readonly SessionStorageService _sut; + + public Length() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public void ReturnsZeroWhenStoreIsEmpty() + { + // Act + var itemCount = _sut.Length(); + + // Assert + Assert.Equal(0, itemCount); + } + + [Fact] + public void ReturnsNumberOfItemsInStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + _sut.SetItem("Item1", item1); + _sut.SetItem("Item2", item2); + + // Act + var itemCount = _sut.Length(); + + // Assert + Assert.Equal(2, itemCount); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs new file mode 100644 index 0000000..0944abe --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs @@ -0,0 +1,57 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class LengthAsync + { + private readonly SessionStorageService _sut; + + public LengthAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public async Task ReturnsZeroWhenStoreIsEmpty() + { + // Act + var itemCount = await _sut.LengthAsync(); + + // Assert + Assert.Equal(0, itemCount); + } + + [Fact] + public async Task ReturnsNumberOfItemsInStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + await _sut.SetItemAsync("Item1", item1); + await _sut.SetItemAsync("Item2", item2); + + // Act + var itemCount = await _sut.LengthAsync(); + + // Assert + Assert.Equal(2, itemCount); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs new file mode 100644 index 0000000..dd29831 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs @@ -0,0 +1,70 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class RemoveItem + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + + private const string Key = "testKey"; + + public RemoveItem() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Action(() => _sut.RemoveItem(key)); + + // assert + Assert.Throws(action); + } + + [Fact] + public void RemovesItemFromStoreIfExists() + { + // Arrange + var data = new TestObject(2, "Jane Smith"); + _sut.SetItem(Key, data); + + // Act + _sut.RemoveItem(Key); + + // Assert + Assert.Equal(0, _storageProvider.Length()); + } + + [Fact] + public void DoesNothingWhenItemDoesNotExistInStore() + { + // Act + _sut.RemoveItem(Key); + + // Assert + Assert.Equal(0, _storageProvider.Length()); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs new file mode 100644 index 0000000..50b5db3 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs @@ -0,0 +1,70 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class RemoveItemAsync + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + + private const string Key = "testKey"; + + public RemoveItemAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Func(async () => await _sut.RemoveItemAsync(key)); + + // assert + Assert.ThrowsAsync(action); + } + + [Fact] + public async Task RemovesItemFromStoreIfExists() + { + // Arrange + var data = new TestObject(2, "Jane Smith"); + await _sut.SetItemAsync(Key, data); + + // Act + await _sut.RemoveItemAsync(Key); + + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } + + [Fact] + public async Task DoesNothingWhenItemDoesNotExistInStore() + { + // Act + await _sut.RemoveItemAsync(Key); + + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs new file mode 100644 index 0000000..3cda711 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs @@ -0,0 +1,235 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class SetItem + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItem() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + const string data = "Data"; + var action = new Action(() => _sut.SetItem(key, data)); + + // assert + Assert.Throws(action); + } + + [Fact] + public void RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (_, _) => onChangingCalled = true; + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } + + [Fact] + public void OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public void OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItemAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public void OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(0, _storageProvider.Length()); + } + + [Theory] + [InlineData("stringTest")] + [InlineData(11)] + [InlineData(11.11)] + public void SavesDataAsJsonToStore(T valueToSave) + { + // Act + _sut.SetItem(Key, valueToSave); + + // Assert + var serializedValue = _storageProvider.GetItem(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(valueToSave, valueFromStore); + } + + [Fact] + public void SavesComplexObjectAsJsonToStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + + // Act + _sut.SetItem(Key, objectToSave); + + // Assert + var serializedObject = _storageProvider.GetItem(Key); + var objectFromStore = _serializer.Deserialize(serializedObject); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(objectToSave.Id, objectFromStore.Id); + Assert.Equal(objectToSave.Name, objectFromStore.Name); + } + + [Fact] + public void SavesNullIntoStore_When_NullValueProvided() + { + // Arrange + var valueToSave = (string)null; + + // Act + _sut.SetItem(Key, valueToSave); + + // Assert + var serializedValue = _storageProvider.GetItem(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Null(valueFromStore); + } + + [Fact] + public void OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + + var serializedValue = _serializer.Serialize(existingValue); + _storageProvider.SetItem(Key, serializedValue); + + // Act + _sut.SetItem(Key, newValue); + + // Assert + var serializedUpdatedValue = _storageProvider.GetItem(Key); + var updatedValue = _serializer.Deserialize(serializedUpdatedValue); + + Assert.Equal(newValue, updatedValue); + } + + [Fact] + public void RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (_, _) => onChangedCalled = true; + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } + + [Fact] + public void OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public void OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItem("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public void OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + _storageProvider.SetItem("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs new file mode 100644 index 0000000..8150bfc --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs @@ -0,0 +1,235 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class SetItemAsync + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItemAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + const string data = "Data"; + var action = new Func(async () => await _sut.SetItemAsync(key, data)); + + // assert + Assert.ThrowsAsync(action); + } + + [Fact] + public async Task RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (sender, args) => onChangingCalled = true; + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } + + [Fact] + public async Task OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public async Task OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public async Task OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } + + [Theory] + [InlineData("stringTest")] + [InlineData(11)] + [InlineData(11.11)] + public async Task SavesDataAsJsonToStore(T valueToSave) + { + // Act + await _sut.SetItemAsync(Key, valueToSave); + + // Assert + var serializedValue = await _storageProvider.GetItemAsync(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, await _storageProvider.LengthAsync()); + Assert.Equal(valueToSave, valueFromStore); + } + + [Fact] + public async Task SavesComplexObjectAsJsonToStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + + // Act + await _sut.SetItemAsync(Key, objectToSave); + + // Assert + var serializedObject = await _storageProvider.GetItemAsync(Key); + var objectFromStore = _serializer.Deserialize(serializedObject); + + Assert.Equal(1, await _storageProvider.LengthAsync()); + Assert.Equal(objectToSave.Id, objectFromStore.Id); + Assert.Equal(objectToSave.Name, objectFromStore.Name); + } + + [Fact] + public async Task SavesNullIntoStore_When_NullValueProvided() + { + // Arrange + var valueToSave = (string)null; + + // Act + await _sut.SetItemAsync(Key, valueToSave); + + // Assert + var serializedValue = await _storageProvider.GetItemAsync(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, await _storageProvider.LengthAsync()); + Assert.Null(valueFromStore); + } + + [Fact] + public async Task OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + + var serializedValue = _serializer.Serialize(existingValue); + await _storageProvider.SetItemAsync(Key, serializedValue); + + // Act + await _sut.SetItemAsync(Key, newValue); + + // Assert + var serializedUpdatedValue = await _storageProvider.GetItemAsync(Key); + var updatedValue = _serializer.Deserialize(serializedUpdatedValue); + + Assert.Equal(newValue, updatedValue); + } + + [Fact] + public async Task RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (sender, args) => onChangedCalled = true; + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } + + [Fact] + public async Task OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public async Task OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public async Task OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + await _storageProvider.SetItemAsync("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/TestAssets/TestObject.cs b/tests/Blazored.SessionStorage.Tests/TestAssets/TestObject.cs new file mode 100644 index 0000000..126a9f1 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/TestAssets/TestObject.cs @@ -0,0 +1,15 @@ +namespace Blazored.SessionStorage.Tests.TestAssets +{ + public class TestObject + { + public int Id { get; set; } + public string Name { get; set; } + + public TestObject() { } + public TestObject(int id, string name) + { + Id = id; + Name = name; + } + } +} From bf135fe179cb8e763297f5846bb1b637b807afda Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Sat, 8 May 2021 13:59:55 +0100 Subject: [PATCH 11/20] Updated readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58e162d..17e9034 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Blazored SessionStorage Blazored SessionStorage is a library that provides access to the browsers session storage APIs for Blazor applications. An additional benefit of using this library is that it will handle serializing and deserializing values when saving or retrieving them. -## Breaking Change (v3 > v4): JsonSerializerOptions +## Breaking Change (v1 > v2): JsonSerializerOptions From v4 onwards we use the default the `JsonSerializerOptions` for `System.Text.Json` instead of using custom ones. This will cause values saved to session storage with v3 to break things. To retain the old settings use the following configuration when adding Blazored SessionStorage to the DI container: From 6ecbc31fd21924142f4ea064f3b0eea7c052f864 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Sat, 8 May 2021 14:07:50 +0100 Subject: [PATCH 12/20] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17e9034..b3ec8f8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Nuget version](https://img.shields.io/nuget/v/blazored.localstorage.svg?logo=nuget)](https://www.nuget.org/packages/Blazored.SessionStorage/) +[![Nuget version](https://img.shields.io/nuget/v/Blazored.SessionStorage.svg?logo=nuget)](https://www.nuget.org/packages/Blazored.SessionStorage/) [![Nuget downloads](https://img.shields.io/nuget/dt/Blazored.SessionStorage?logo=nuget)](https://www.nuget.org/packages/Blazored.SessionStorage/) ![Build & Test Main](https://github.com/Blazored/SessionStorage/workflows/Build%20&%20Test%20Main/badge.svg) @@ -192,4 +192,4 @@ public class IndexPageTests : TestContext Assert.Equal(inputName, name); } } -``` \ No newline at end of file +``` From da46b32a1cfefc4ce6c8c37233c6dcd7ce4fc0c9 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Sat, 15 May 2021 14:54:55 +0100 Subject: [PATCH 13/20] Added SetItemAsString methods --- README.md | 10 +- .../ISessionStorageService.cs | 10 +- .../ISyncSessionStorageService.cs | 10 +- .../SessionStorageService.cs | 38 +++- .../SetItemAsString.cs | 204 +++++++++++++++++ .../SetItemAsStringAsync.cs | 205 ++++++++++++++++++ 6 files changed, 473 insertions(+), 4 deletions(-) create mode 100644 tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs create mode 100644 tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs diff --git a/README.md b/README.md index b3ec8f8..e0f165b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ # Blazored SessionStorage Blazored SessionStorage is a library that provides access to the browsers session storage APIs for Blazor applications. An additional benefit of using this library is that it will handle serializing and deserializing values when saving or retrieving them. -## Breaking Change (v1 > v2): JsonSerializerOptions +## Breaking Change (v1 > v2) + +### JsonSerializerOptions From v4 onwards we use the default the `JsonSerializerOptions` for `System.Text.Json` instead of using custom ones. This will cause values saved to session storage with v3 to break things. To retain the old settings use the following configuration when adding Blazored SessionStorage to the DI container: @@ -21,6 +23,10 @@ builder.Services.AddBlazoredSessionStorage(config => ); ``` +### SetItem[Async] method now serializes string values +Prior to v2 we bypassed the serialization of string values as it seemed a pointless as string can be stored directly. However, this led to some edge cases where nullable strings were being saved as the string `"null"`. Then when retrieved, instead of being null the value was `"null"`. By serializing strings this issue is taken care of. +For those who wish to save raw string values, a new method `SetValueAsString[Async]` is available. This will save a string value without attempting to serialize it and will throw an exception if a null string is attempted to be saved. + ## Installing To install the package add the following line to you csproj file replacing x.x.x with the latest version number (found at the top of this file): @@ -117,6 +123,7 @@ The APIs available are: - asynchronous via `ISessionStorageService`: - SetItemAsync() + - SetItemAsStringAsync() - GetItemAsync() - GetItemAsStringAsync() - RemoveItemAsync() @@ -127,6 +134,7 @@ The APIs available are: - synchronous via `ISyncSessionStorageService` (Synchronous methods are **only** available in Blazor WebAssembly): - SetItem() + - SetItemAsString() - GetItem() - GetItemAsString() - RemoveItem() diff --git a/src/Blazored.SessionStorage/ISessionStorageService.cs b/src/Blazored.SessionStorage/ISessionStorageService.cs index 476435f..f9d7a3f 100644 --- a/src/Blazored.SessionStorage/ISessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISessionStorageService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace Blazored.SessionStorage @@ -60,6 +60,14 @@ public interface ISessionStorageService /// A representing the completion of the operation. ValueTask SetItemAsync(string key, T data); + /// + /// Sets or updates the in session storage with the specified . Does not serialize the value before storing. + /// + /// A value specifying the name of the storage slot to use + /// The string to be saved + /// + ValueTask SetItemAsStringAsync(string key, string data); + event EventHandler Changing; event EventHandler Changed; } diff --git a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs index f1efb32..0017e89 100644 --- a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Blazored.SessionStorage { @@ -56,6 +56,14 @@ public interface ISyncSessionStorageService /// The data to be saved void SetItem(string key, T data); + /// + /// Sets or updates the in session storage with the specified . Does not serialize the value before storing. + /// + /// A value specifying the name of the storage slot to use + /// The string to be saved + /// + void SetItemAsString(string key, string data); + event EventHandler Changing; event EventHandler Changed; } diff --git a/src/Blazored.SessionStorage/SessionStorageService.cs b/src/Blazored.SessionStorage/SessionStorageService.cs index 8bdd847..64434c3 100644 --- a/src/Blazored.SessionStorage/SessionStorageService.cs +++ b/src/Blazored.SessionStorage/SessionStorageService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.Serialization; @@ -32,6 +32,24 @@ public async ValueTask SetItemAsync(string key, T data) RaiseOnChanged(key, e.OldValue, data); } + public async ValueTask SetItemAsStringAsync(string key, string data) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); + + if (data is null) + throw new ArgumentNullException(nameof(data)); + + var e = await RaiseOnChangingAsync(key, data).ConfigureAwait(false); + + if (e.Cancel) + return; + + await _storageProvider.SetItemAsync(key, data).ConfigureAwait(false); + + RaiseOnChanged(key, e.OldValue, data); + } + public async ValueTask GetItemAsync(string key) { if (string.IsNullOrWhiteSpace(key)) @@ -98,6 +116,24 @@ public void SetItem(string key, T data) RaiseOnChanged(key, e.OldValue, data); } + public void SetItemAsString(string key, string data) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); + + if (data is null) + throw new ArgumentNullException(nameof(data)); + + var e = RaiseOnChangingSync(key, data); + + if (e.Cancel) + return; + + _storageProvider.SetItem(key, data); + + RaiseOnChanged(key, e.OldValue, data); + } + public T GetItem(string key) { if (string.IsNullOrWhiteSpace(key)) diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs new file mode 100644 index 0000000..a00c9d4 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs @@ -0,0 +1,204 @@ +using System; +using System.Text.Json; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class SetItemAsString + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItemAsString() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + const string data = "Data"; + var action = new Action(() => _sut.SetItemAsString(key, data)); + + // assert + Assert.Throws(action); + } + + [Fact] + public void ThrowsArgumentNullException_When_DataIsNull() + { + // arrange / act + var data = (string)null; + var action = new Action(() => _sut.SetItemAsString("MyValue", data)); + + // assert + Assert.Throws(action); + } + + [Fact] + public void RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (_, _) => onChangingCalled = true; + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } + + [Fact] + public void OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public void OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItemAsString("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public void OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(0, _storageProvider.Length()); + } + + [Fact] + public void SavesDataToStore() + { + // Act + var valueToSave = "StringValue"; + _sut.SetItemAsString(Key, valueToSave); + + // Assert + var valueFromStore = _storageProvider.GetItem(Key); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(valueToSave, valueFromStore); + } + + [Fact] + public void OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + + _storageProvider.SetItem(Key, existingValue); + + // Act + _sut.SetItemAsString(Key, newValue); + + // Assert + var updatedValue = _storageProvider.GetItem(Key); + + Assert.Equal(newValue, updatedValue); + } + + [Fact] + public void RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (_, _) => onChangedCalled = true; + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } + + [Fact] + public void OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public void OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItemAsString("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public void OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + _storageProvider.SetItem("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); + } + } +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs new file mode 100644 index 0000000..7a82c1a --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs @@ -0,0 +1,205 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.Testing; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +{ + public class SetItemAsStringAsync + { + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItemAsStringAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + const string data = "Data"; + var action = new Func(async () => await _sut.SetItemAsStringAsync(key, data)); + + // assert + Assert.ThrowsAsync(action); + } + + [Fact] + public void ThrowsArgumentNullException_When_DataIsNull() + { + // arrange / act + var data = (string)null; + var action = new Func(async () => await _sut.SetItemAsStringAsync("MyValue", data)); + + // assert + Assert.ThrowsAsync(action); + } + + [Fact] + public async Task RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (_, _) => onChangingCalled = true; + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } + + [Fact] + public async Task OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public async Task OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public async Task OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(0, _storageProvider.Length()); + } + + [Fact] + public async Task SavesDataToStore() + { + // Act + var valueToSave = "StringValue"; + await _sut.SetItemAsStringAsync(Key, valueToSave); + + // Assert + var valueFromStore = _storageProvider.GetItem(Key); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(valueToSave, valueFromStore); + } + + [Fact] + public async Task OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + + _storageProvider.SetItem(Key, existingValue); + + // Act + await _sut.SetItemAsStringAsync(Key, newValue); + + // Assert + var updatedValue = _storageProvider.GetItem(Key); + + Assert.Equal(newValue, updatedValue); + } + + [Fact] + public async Task RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (_, _) => onChangedCalled = true; + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } + + [Fact] + public async Task OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } + + [Fact] + public async Task OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } + + [Fact] + public async Task OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + _storageProvider.SetItem("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); + } + } +} From 27cac1df6fb87f564c9c320c44596eceb448490c Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Fri, 8 Oct 2021 22:14:40 +0100 Subject: [PATCH 14/20] Moved test extensions to seperate project as per local storage --- .github/workflows/ci-main.yml | 78 ++++++------------ .github/workflows/ci-pr.yml | 9 +- .gitignore | 2 + Blazored.SessionStorage.sln | 21 ++++- samples/bUnitExample/bUnitExample.csproj | 4 +- .../BUnitSessionStorageTestExtensions.cs} | 29 +++---- ...zored.SessionStorage.TestExtensions.csproj | 53 ++++++++++++ .../Blazored.SessionStorage.snk | Bin 0 -> 596 bytes .../InMemoryStorageProvider.cs | 20 ++--- .../Blazored.SessionStorage.csproj | 16 +++- .../BrowserStorageProvider.cs | 17 ++-- .../IStorageProvider.cs | 19 +++-- src/Blazored.SessionStorage/PublicKey.snk | Bin 160 -> 0 bytes src/Blazored.SessionStorage/PublicKey.txt | 5 -- .../Serialization/SystemTextJsonSerializer.cs | 12 ++- .../Blazored.SessionStorage.Tests.csproj | 1 + .../SessionStorageServiceTests/Clear.cs | 4 +- .../SessionStorageServiceTests/ClearAsync.cs | 3 +- .../SessionStorageServiceTests/ContainsKey.cs | 4 +- .../ContainsKeyAsync.cs | 3 +- .../SessionStorageServiceTests/GetItem.cs | 5 +- .../GetItemAsString.cs | 5 +- .../GetItemAsStringAsync.cs | 4 +- .../GetItemAsync.cs | 4 +- .../SessionStorageServiceTests/Key.cs | 4 +- .../SessionStorageServiceTests/KeyAsync.cs | 3 +- .../SessionStorageServiceTests/Length.cs | 4 +- .../SessionStorageServiceTests/LengthAsync.cs | 3 +- .../SessionStorageServiceTests/RemoveItem.cs | 5 +- .../RemoveItemAsync.cs | 4 +- .../SessionStorageServiceTests/SetItem.cs | 5 +- .../SetItemAsString.cs | 2 +- .../SetItemAsStringAsync.cs | 2 +- .../SetItemAsync.cs | 4 +- 34 files changed, 195 insertions(+), 159 deletions(-) rename src/{Blazored.SessionStorage/Testing/BUnitLocalStorageTestExtensions.cs => Blazored.SessionStorage.TestExtensions/BUnitSessionStorageTestExtensions.cs} (52%) create mode 100644 src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj create mode 100644 src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.snk rename src/{Blazored.SessionStorage/Testing => Blazored.SessionStorage.TestExtensions}/InMemoryStorageProvider.cs (66%) delete mode 100644 src/Blazored.SessionStorage/PublicKey.snk delete mode 100644 src/Blazored.SessionStorage/PublicKey.txt diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 3f35b26..2287183 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -11,8 +11,6 @@ env: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true PROJECT_NAME: SessionStorage - GITHUB_FEED: https://nuget.pkg.github.com/Blazored/ - GITHUB_USER: chrissainty GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NUGET_FEED: https://api.nuget.org/v3/index.json NUGET_KEY: ${{ secrets.NUGET_KEY }} @@ -38,60 +36,24 @@ jobs: - name: Build run: dotnet build -c Release --no-restore src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.csproj - - name: Test - run: dotnet test -c Release + - name: Build Test Extensions + run: dotnet build -c Release --no-restore src/Blazored.$PROJECT_NAME.TestExtensions/Blazored.$PROJECT_NAME.TestExtensions.csproj - - name: Pack - run: dotnet pack -v normal -c Release --no-restore --include-symbols --include-source -p:PackageVersion=$GITHUB_RUN_ID src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.*proj + - name: Test Local Storage + run: dotnet test -c Release --no-restore --verbosity normal tests/Blazored.$PROJECT_NAME.Tests - - name: Publish Sample Site - run: dotnet publish -c Release samples/BlazorWebAssembly/BlazorWebAssembly.csproj - - - name: Rewrite base href - uses: SteveSandersonMS/ghaction-rewrite-base-href@v1 - with: - html_path: samples/BlazorWebAssembly/bin/Release/net5.0/publish/wwwroot/index.html - base_href: /${{ env.PROJECT_NAME }}/ - - - name: Deploy to Github Pages - uses: JamesIves/github-pages-deploy-action@releases/v3 - with: - ACCESS_TOKEN: $GITHUB_TOKEN - BASE_BRANCH: main # The branch the action should deploy from. - BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: samples/BlazorWebAssembly/bin/Release/net5.0/publish/wwwroot # The folder the action should deploy. - SINGLE_COMMIT: true - - - name: Upload Artifact - uses: actions/upload-artifact@v2 - with: - name: nupkg - path: ./src/Blazored.${{ env.PROJECT_NAME }}/bin/Release/*.nupkg - prerelease: - needs: build - if: github.event_name != 'release' - runs-on: ubuntu-latest - steps: - - name: Download Artifact - uses: actions/download-artifact@v1 - with: - name: nupkg - - name: Push to GitHub Feed - run: | - for f in ./nupkg/*.nupkg - do - curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED - done deploy: needs: build if: github.event_name == 'release' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: dotnet-version: ${{ env.NETCORE_VERSION }} + - name: Create Release NuGet package run: | arrTag=(${GITHUB_REF//\// }) @@ -100,11 +62,25 @@ jobs: VERSION="${VERSION//v}" echo Clean Version: $VERSION dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.*proj - - name: Push to GitHub Feed - run: | - for f in ./nupkg/*.nupkg - do - curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED - done + dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/Blazored.$PROJECT_NAME.TestExtensions/Blazored.$PROJECT_NAME.TestExtensions.csproj + - name: Push to NuGet Feed - run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY + run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --api-key $NUGET_KEY --skip-duplicate + + - name: Publish Sample Site + run: dotnet publish -c Release samples/BlazorWebAssembly/BlazorWebAssembly.csproj + + - name: Rewrite base href + uses: SteveSandersonMS/ghaction-rewrite-base-href@v1 + with: + html_path: samples/BlazorWebAssembly/bin/Release/net5.0/publish/wwwroot/index.html + base_href: /${{ env.PROJECT_NAME }}/ + + - name: Deploy to Github Pages + uses: JamesIves/github-pages-deploy-action@releases/v3 + with: + ACCESS_TOKEN: $GITHUB_TOKEN + BASE_BRANCH: main # The branch the action should deploy from. + BRANCH: gh-pages # The branch the action should deploy to. + FOLDER: samples/BlazorWebAssembly/bin/Release/net5.0/publish/wwwroot # The folder the action should deploy. + SINGLE_COMMIT: true diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 7d718cc..63c70fd 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -5,7 +5,7 @@ on: branches: [ main ] env: - NETCORE_VERSION: '5.0.202' + NETCORE_VERSION: '5.0.x' PROJECT_NAME: Blazored.SessionStorage jobs: @@ -23,8 +23,11 @@ jobs: - name: Restoring packages... run: dotnet restore - - name: Building project... + - name: Building Session Storage... run: dotnet build --configuration Release --no-restore src/$PROJECT_NAME/$PROJECT_NAME.csproj + - name: Build Session Storage Test Extensions + run: dotnet build -c Release --no-restore src/$PROJECT_NAME.TestExtensions/$PROJECT_NAME.TestExtensions.csproj + - name: Testing... - run: dotnet test --no-restore --verbosity normal \ No newline at end of file + run: dotnet test -c Release --no-restore --verbosity normal tests/$PROJECT_NAME.Tests \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3e759b7..d2d12b1 100644 --- a/.gitignore +++ b/.gitignore @@ -179,6 +179,8 @@ PublishScripts/ # NuGet Packages *.nupkg +*.snupkg + # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. diff --git a/Blazored.SessionStorage.sln b/Blazored.SessionStorage.sln index e084bba..4e05906 100644 --- a/Blazored.SessionStorage.sln +++ b/Blazored.SessionStorage.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28531.58 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31717.71 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazored.SessionStorage", "src\Blazored.SessionStorage\Blazored.SessionStorage.csproj", "{EFA12DC3-35DE-449C-88F2-2BD7576CAF2C}" EndProject @@ -13,6 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServer", "samples\Bla EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9170D7A9-70CE-48E3-88A3-F11D2983103E}" ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore .github\workflows\ci-main.yml = .github\workflows\ci-main.yml .github\workflows\ci-pr.yml = .github\workflows\ci-pr.yml README.md = README.md @@ -23,7 +24,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B82A5126 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazored.SessionStorage.Tests", "tests\Blazored.SessionStorage.Tests\Blazored.SessionStorage.Tests.csproj", "{5A6A013E-325D-4A5F-B2FA-659B4FD2BDBC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bUnitExample", "samples\bUnitExample\bUnitExample.csproj", "{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bUnitExample", "samples\bUnitExample\bUnitExample.csproj", "{10F6FB83-3135-4B73-B0EB-D008A31AE8FA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazored.SessionStorage.TestExtensions", "src\Blazored.SessionStorage.TestExtensions\Blazored.SessionStorage.TestExtensions.csproj", "{528AE63D-0043-4A63-9151-92107397EE02}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -95,6 +98,18 @@ Global {10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|x64.Build.0 = Release|Any CPU {10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|x86.ActiveCfg = Release|Any CPU {10F6FB83-3135-4B73-B0EB-D008A31AE8FA}.Release|x86.Build.0 = Release|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Debug|x64.ActiveCfg = Debug|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Debug|x64.Build.0 = Debug|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Debug|x86.ActiveCfg = Debug|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Debug|x86.Build.0 = Debug|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Release|Any CPU.Build.0 = Release|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Release|x64.ActiveCfg = Release|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Release|x64.Build.0 = Release|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Release|x86.ActiveCfg = Release|Any CPU + {528AE63D-0043-4A63-9151-92107397EE02}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/bUnitExample/bUnitExample.csproj b/samples/bUnitExample/bUnitExample.csproj index 6d5685c..2d94f8e 100644 --- a/samples/bUnitExample/bUnitExample.csproj +++ b/samples/bUnitExample/bUnitExample.csproj @@ -5,8 +5,7 @@ - - + @@ -20,6 +19,7 @@ + diff --git a/src/Blazored.SessionStorage/Testing/BUnitLocalStorageTestExtensions.cs b/src/Blazored.SessionStorage.TestExtensions/BUnitSessionStorageTestExtensions.cs similarity index 52% rename from src/Blazored.SessionStorage/Testing/BUnitLocalStorageTestExtensions.cs rename to src/Blazored.SessionStorage.TestExtensions/BUnitSessionStorageTestExtensions.cs index efaadcf..4de3433 100644 --- a/src/Blazored.SessionStorage/Testing/BUnitLocalStorageTestExtensions.cs +++ b/src/Blazored.SessionStorage.TestExtensions/BUnitSessionStorageTestExtensions.cs @@ -1,16 +1,17 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Blazored.SessionStorage; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.TestExtensions; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Diagnostics.CodeAnalysis; -using Blazored.SessionStorage; -using Blazored.SessionStorage.Testing; namespace Bunit { [ExcludeFromCodeCoverage] public static class BUnitSessionStorageTestExtensions + { public static ISessionStorageService AddBlazoredSessionStorage(this TestContextBase context) => AddBlazoredSessionStorage(context, null); @@ -18,20 +19,16 @@ public static ISessionStorageService AddBlazoredSessionStorage(this TestContextB public static ISessionStorageService AddBlazoredSessionStorage(this TestContextBase context, Action configure) { if (context is null) - throw new ArgumentNullException(nameof(context)); + throw new ArgumentNullException(nameof(context)); + + var sessionStorageOptions = new SessionStorageOptions(); + configure?.Invoke(sessionStorageOptions); + sessionStorageOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); - context.Services - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .Configure(configureOptions => - { - configure?.Invoke(configureOptions); - configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); - }); + var localStorageService = new SessionStorageService(new InMemoryStorageProvider(), new SystemTextJsonSerializer(sessionStorageOptions)); + context.Services.AddSingleton(localStorageService); - return context.Services.GetService(); + return localStorageService; } } } diff --git a/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj b/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj new file mode 100644 index 0000000..f064f63 --- /dev/null +++ b/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj @@ -0,0 +1,53 @@ + + + + netstandard2.1;net5.0 + + Chris Sainty + + Copyright 2020 (c) Chris Sainty. All rights reserved. + A testing library to provide helper extensions for Blazored.SessionStorage + true + snupkg + + + Blazored.SessionStorage.TestExtensions + Blazored;Blazor;Razor;Components;SessionStorage;Session Storage;ASP.NET Core;CSharp;Web + MIT + https://github.com/Blazored/SessionStorage + icon.png + git + https://github.com/Blazored/SessionStorage + + + true + true + true + Blazored.SessionStorage.snk + + + + + + + + + + + + + + + + + <_Parameter1> + Blazored.SessionStorage.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007527e122cc36dc13c695a4f43b7c2da3f631aed456ed309140c0d52262323e25d84bef7feddc8bd29cfe46ec652186cb10e059eedabf2ff000b977a2376a613dccfb092de6c243e0888db4c66a084124b2c1799bda4bbb2f70fed0382fc1cbdafa6dc0f4baccdc2cee55234f8a5ad76645c315523fee5352d9f01036e48b13e3 + + + + + + + + + diff --git a/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.snk b/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.snk new file mode 100644 index 0000000000000000000000000000000000000000..18e2ce3056b7ac1a4950e9cd2874f423e197e0d5 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097XC*dN@Hrx}&m8A4Ld@ZB)F|O2B?J$u* zz||sRGCn2POYeW}+>6qj{zmL&A%@Ek;92h4zc26rxp$&BYGFOh`w1=P!b9MQjkLyU z2tg#W!Fik7OS>;{{?Irt!OPnEZNT)p%-k&QRU=P|TGwVp!xd6L?o(3P@DMiSixcDb zt^>MkhmWu1(SrN*jcP{D4WaB3D-(A8AKI~^zhV#M#A~w*7qq&wAOA6UeNlJhxKgAu zI8cBn8@Q-Z#xCpg#j=cSld0UJAY7goaqnPnr!=w^e{qWGCOwx&HfbyKeTdH$p|_yH zN-#qZauNGh)Nh`fyON*fg0*M}6)pFHu!((3ShrkRuHQP;K55xA`PX@pXHwQi0nj+i zIT;V@@*i`vm(K~7Eg$Tyq)NpfY_d&$KO3WWIW>+77?wD zJMvLM69^UTy``CbNS>DAT}1Oc$H7PDHz4CWiVre`iH8-F1^Q!gb=b%UTovXOt4ws` zq4dX5E5ZohBuXYBMNT33s|$?;3OblFAQgz+LgMM|1Y>$S{LVUk;(}2ZXjdSiUjch3 zTiH!n<98=1rZr`~17ue9?<0Nf|7=Y$fp5av;aWUYr)CX3o1aJG)4pDcE%R@g4vL~L z@2Ej)ZtUCuI+i)bH&4N1TfMxYE _dataStore.Clear(); - public ValueTask ClearAsync() + public ValueTask ClearAsync(CancellationToken? cancellationToken = null) { _dataStore.Clear(); return new ValueTask(Task.CompletedTask); @@ -21,31 +21,31 @@ public ValueTask ClearAsync() public bool ContainKey(string key) => _dataStore.ContainsKey(key); - public ValueTask ContainKeyAsync(string key) + public ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null) => new ValueTask(ContainKey(key)); - public string GetItem(string key) + public string GetItem(string key) => _dataStore.ContainsKey(key) ? _dataStore[key] : default; - public ValueTask GetItemAsync(string key) + public ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) => new ValueTask(GetItem(key)); public string Key(int index) => index > _dataStore.Count - 1 ? default : _dataStore.ElementAt(index).Key; - public ValueTask KeyAsync(int index) + public ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null) => new ValueTask(Key(index)); public int Length() => _dataStore.Count; - public ValueTask LengthAsync() + public ValueTask LengthAsync(CancellationToken? cancellationToken = null) => new ValueTask(Length()); public void RemoveItem(string key) => _dataStore.Remove(key); - public ValueTask RemoveItemAsync(string key) + public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) { RemoveItem(key); return new ValueTask(Task.CompletedTask); @@ -63,7 +63,7 @@ public void SetItem(string key, string data) } } - public ValueTask SetItemAsync(string key, string data) + public ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null) { SetItem(key, data); return new ValueTask(Task.CompletedTask); diff --git a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj index 8191ad3..194b03e 100644 --- a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj +++ b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj @@ -35,12 +35,10 @@ - - @@ -49,7 +47,19 @@ - + + <_Parameter1> + Blazored.SessionStorage.TestExtensions, PublicKey=00240000048000009400000006020000002400005253413100040000010001007527e122cc36dc13c695a4f43b7c2da3f631aed456ed309140c0d52262323e25d84bef7feddc8bd29cfe46ec652186cb10e059eedabf2ff000b977a2376a613dccfb092de6c243e0888db4c66a084124b2c1799bda4bbb2f70fed0382fc1cbdafa6dc0f4baccdc2cee55234f8a5ad76645c315523fee5352d9f01036e48b13e3 + + + + + + + <_Parameter1> + Blazored.SessionStorage.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007527e122cc36dc13c695a4f43b7c2da3f631aed456ed309140c0d52262323e25d84bef7feddc8bd29cfe46ec652186cb10e059eedabf2ff000b977a2376a613dccfb092de6c243e0888db4c66a084124b2c1799bda4bbb2f70fed0382fc1cbdafa6dc0f4baccdc2cee55234f8a5ad76645c315523fee5352d9f01036e48b13e3 + + diff --git a/src/Blazored.SessionStorage/BrowserStorageProvider.cs b/src/Blazored.SessionStorage/BrowserStorageProvider.cs index 7f68520..626f9a7 100644 --- a/src/Blazored.SessionStorage/BrowserStorageProvider.cs +++ b/src/Blazored.SessionStorage/BrowserStorageProvider.cs @@ -1,5 +1,6 @@ -using Microsoft.JSInterop; +using Microsoft.JSInterop; using System; +using System.Threading; using System.Threading.Tasks; namespace Blazored.SessionStorage @@ -15,25 +16,25 @@ public BrowserStorageProvider(IJSRuntime jSRuntime) _jSInProcessRuntime = jSRuntime as IJSInProcessRuntime; } - public ValueTask ClearAsync() + public ValueTask ClearAsync(CancellationToken? cancellationToken = null) => _jSRuntime.InvokeVoidAsync("sessionStorage.clear"); - public ValueTask GetItemAsync(string key) + public ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) => _jSRuntime.InvokeAsync("sessionStorage.getItem", key); - public ValueTask KeyAsync(int index) + public ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null) => _jSRuntime.InvokeAsync("sessionStorage.key", index); - public ValueTask ContainKeyAsync(string key) + public ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null) => _jSRuntime.InvokeAsync("sessionStorage.hasOwnProperty", key); - public ValueTask LengthAsync() + public ValueTask LengthAsync(CancellationToken? cancellationToken = null) => _jSRuntime.InvokeAsync("eval", "sessionStorage.length"); - public ValueTask RemoveItemAsync(string key) + public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) => _jSRuntime.InvokeVoidAsync("sessionStorage.removeItem", key); - public ValueTask SetItemAsync(string key, string data) + public ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null) => _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", key, data); public void Clear() diff --git a/src/Blazored.SessionStorage/IStorageProvider.cs b/src/Blazored.SessionStorage/IStorageProvider.cs index a45f204..df476de 100644 --- a/src/Blazored.SessionStorage/IStorageProvider.cs +++ b/src/Blazored.SessionStorage/IStorageProvider.cs @@ -1,22 +1,23 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace Blazored.SessionStorage { internal interface IStorageProvider { void Clear(); - ValueTask ClearAsync(); + ValueTask ClearAsync(CancellationToken? cancellationToken = null); bool ContainKey(string key); - ValueTask ContainKeyAsync(string key); + ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null); string GetItem(string key); - ValueTask GetItemAsync(string key); + ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null); string Key(int index); - ValueTask KeyAsync(int index); + ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null); int Length(); - ValueTask LengthAsync(); + ValueTask LengthAsync(CancellationToken? cancellationToken = null); void RemoveItem(string key); - ValueTask RemoveItemAsync(string key); + ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null); void SetItem(string key, string data); - ValueTask SetItemAsync(string key, string data); + ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null); } -} \ No newline at end of file +} diff --git a/src/Blazored.SessionStorage/PublicKey.snk b/src/Blazored.SessionStorage/PublicKey.snk deleted file mode 100644 index 031abef3d62315bb6368ee49f2f9f59da8f6df16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmV;R0AK$ABme*efB*oL000060ssI2Bme+XQ$aBR1ONa50097XC*dN@Hrx}&m8A4L zd@ZB)F|O2B?J$u*z||sRGCn2POYeW}+>6qj{zmL&A%@Ek;92h4zc26rxp$&BYGFOh z`w1=P!b9MQjkLyU2tg#W!Fik7OS>;{{?Irt!OPnEZNT)p%-k&QRU=P|TGwVp!xd6L O?o(3P@DMiSixcC{1xJVg diff --git a/src/Blazored.SessionStorage/PublicKey.txt b/src/Blazored.SessionStorage/PublicKey.txt deleted file mode 100644 index 0a9702e..0000000 --- a/src/Blazored.SessionStorage/PublicKey.txt +++ /dev/null @@ -1,5 +0,0 @@ - -Microsoft (R) .NET Framework Strong Name Utility Version 4.0.30319.0 -Copyright (c) Microsoft Corporation. All rights reserved. - -Public key token is 6f8d36f00e4891c3 diff --git a/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs b/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs index 8100bab..5ce56c0 100644 --- a/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs +++ b/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs @@ -1,14 +1,7 @@ using Blazored.SessionStorage.StorageOptions; using Microsoft.Extensions.Options; -using System.Runtime.CompilerServices; using System.Text.Json; -[assembly: InternalsVisibleTo("Blazored.SessionStorage.Tests, PublicKey=" + -"00240000048000009400000006020000002400005253413100040000010001007527e122cc36dc" + -"13c695a4f43b7c2da3f631aed456ed309140c0d52262323e25d84bef7feddc8bd29cfe46ec6521" + -"86cb10e059eedabf2ff000b977a2376a613dccfb092de6c243e0888db4c66a084124b2c1799bda" + -"4bbb2f70fed0382fc1cbdafa6dc0f4baccdc2cee55234f8a5ad76645c315523fee5352d9f01036" + -"e48b13e3")] namespace Blazored.SessionStorage.Serialization { internal class SystemTextJsonSerializer : IJsonSerializer @@ -20,6 +13,11 @@ public SystemTextJsonSerializer(IOptions options) _options = options.Value.JsonSerializerOptions; } + public SystemTextJsonSerializer(SessionStorageOptions sessionStorageOptions) + { + _options = sessionStorageOptions.JsonSerializerOptions; + } + public T Deserialize(string data) => JsonSerializer.Deserialize(data, _options); diff --git a/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj b/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj index 01c5fd1..085b1c3 100644 --- a/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj +++ b/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj @@ -27,6 +27,7 @@ + diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs index c29f9f3..a7c8331 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs @@ -1,10 +1,8 @@ -using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs index 159894c..69a71e4 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs @@ -1,10 +1,9 @@ -using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs index d56f77a..b09f482 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs @@ -1,10 +1,8 @@ -using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs index 9fb4474..c2a9cc4 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs @@ -1,10 +1,9 @@ -using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs index 850c041..6052294 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs @@ -1,10 +1,9 @@ -using System; +using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs index b3efe67..253ed78 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs @@ -1,10 +1,9 @@ -using System; +using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs index c024edc..9a7b3d7 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs index 73bbe87..cba032e 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs index 5413869..5082df1 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs @@ -1,10 +1,8 @@ -using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs index b1ca855..86dec29 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs @@ -1,10 +1,9 @@ -using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs index dbb4664..94d189d 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs @@ -1,10 +1,8 @@ -using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs index 0944abe..6cc547e 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs @@ -1,10 +1,9 @@ -using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs index dd29831..bc088d4 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs @@ -1,10 +1,9 @@ -using System; +using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs index 50b5db3..56f27c4 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs index 3cda711..f722851 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs @@ -1,10 +1,9 @@ -using System; +using System; using System.Text.Json; -using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs index a00c9d4..24ec2fd 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs @@ -3,7 +3,7 @@ using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Microsoft.Extensions.Options; using Moq; using Xunit; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs index 7a82c1a..4a5d0a2 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs @@ -4,7 +4,7 @@ using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Microsoft.Extensions.Options; using Moq; using Xunit; diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs index 8150bfc..df49036 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Text.Json; using System.Threading.Tasks; using Blazored.SessionStorage.JsonConverters; using Blazored.SessionStorage.Serialization; using Blazored.SessionStorage.StorageOptions; -using Blazored.SessionStorage.Testing; +using Blazored.SessionStorage.TestExtensions; using Blazored.SessionStorage.Tests.TestAssets; using Microsoft.Extensions.Options; using Moq; From fbc31d4c29fa09fc2728e53c123d8f6e40d6b559 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Fri, 8 Oct 2021 23:05:24 +0100 Subject: [PATCH 15/20] Added missing cancellation token support --- .../BrowserStorageProvider.cs | 14 +++---- .../ISessionStorageService.cs | 19 +++++----- .../SessionStorageService.cs | 37 ++++++++++--------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/Blazored.SessionStorage/BrowserStorageProvider.cs b/src/Blazored.SessionStorage/BrowserStorageProvider.cs index 626f9a7..6c1e398 100644 --- a/src/Blazored.SessionStorage/BrowserStorageProvider.cs +++ b/src/Blazored.SessionStorage/BrowserStorageProvider.cs @@ -17,25 +17,25 @@ public BrowserStorageProvider(IJSRuntime jSRuntime) } public ValueTask ClearAsync(CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeVoidAsync("sessionStorage.clear"); + => _jSRuntime.InvokeVoidAsync("sessionStorage.clear", cancellationToken ?? CancellationToken.None); public ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("sessionStorage.getItem", key); + => _jSRuntime.InvokeAsync("sessionStorage.getItem", cancellationToken ?? CancellationToken.None, key); public ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("sessionStorage.key", index); + => _jSRuntime.InvokeAsync("sessionStorage.key", cancellationToken ?? CancellationToken.None, index); public ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("sessionStorage.hasOwnProperty", key); + => _jSRuntime.InvokeAsync("sessionStorage.hasOwnProperty", cancellationToken ?? CancellationToken.None, key); public ValueTask LengthAsync(CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("eval", "sessionStorage.length"); + => _jSRuntime.InvokeAsync("eval", cancellationToken ?? CancellationToken.None, "sessionStorage.length"); public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeVoidAsync("sessionStorage.removeItem", key); + => _jSRuntime.InvokeVoidAsync("sessionStorage.removeItem", cancellationToken ?? CancellationToken.None, key); public ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", key, data); + => _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", cancellationToken ?? CancellationToken.None, key, data); public void Clear() { diff --git a/src/Blazored.SessionStorage/ISessionStorageService.cs b/src/Blazored.SessionStorage/ISessionStorageService.cs index f9d7a3f..c61c98c 100644 --- a/src/Blazored.SessionStorage/ISessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISessionStorageService.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Blazored.SessionStorage @@ -9,48 +10,48 @@ public interface ISessionStorageService /// Clears all data from session storage. /// /// A representing the completion of the operation. - ValueTask ClearAsync(); + ValueTask ClearAsync(CancellationToken? cancellationToken = null); /// /// Retrieve the specified data from session storage and deseralise it to the specfied type. /// /// A value specifying the name of the session storage slot to use /// A representing the completion of the operation. - ValueTask GetItemAsync(string key); + ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null); /// /// Retrieve the specified data from session storage as a . /// /// A value specifying the name of the storage slot to use /// A representing the completion of the operation. - ValueTask GetItemAsStringAsync(string key); + ValueTask GetItemAsStringAsync(string key, CancellationToken? cancellationToken = null); /// /// Return the name of the key at the specified . /// /// /// A representing the completion of the operation. - ValueTask KeyAsync(int index); + ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null); /// /// Checks if the exists in session storage, but does not check its value. /// /// A value specifying the name of the storage slot to use /// A representing the completion of the operation. - ValueTask ContainKeyAsync(string key); + ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null); /// /// The number of items stored in session storage. /// /// A representing the completion of the operation. - ValueTask LengthAsync(); + ValueTask LengthAsync(CancellationToken? cancellationToken = null); /// /// Remove the data with the specified . /// /// A value specifying the name of the storage slot to use /// A representing the completion of the operation. - ValueTask RemoveItemAsync(string key); + ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null); /// /// Sets or updates the in session storage with the specified . @@ -58,7 +59,7 @@ public interface ISessionStorageService /// A value specifying the name of the storage slot to use /// The data to be saved /// A representing the completion of the operation. - ValueTask SetItemAsync(string key, T data); + ValueTask SetItemAsync(string key, T data, CancellationToken? cancellationToken = null); /// /// Sets or updates the in session storage with the specified . Does not serialize the value before storing. @@ -66,7 +67,7 @@ public interface ISessionStorageService /// A value specifying the name of the storage slot to use /// The string to be saved /// - ValueTask SetItemAsStringAsync(string key, string data); + ValueTask SetItemAsStringAsync(string key, string data, CancellationToken? cancellationToken = null); event EventHandler Changing; event EventHandler Changed; diff --git a/src/Blazored.SessionStorage/SessionStorageService.cs b/src/Blazored.SessionStorage/SessionStorageService.cs index 64434c3..d020994 100644 --- a/src/Blazored.SessionStorage/SessionStorageService.cs +++ b/src/Blazored.SessionStorage/SessionStorageService.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Blazored.SessionStorage.Serialization; @@ -16,7 +17,7 @@ public SessionStorageService(IStorageProvider storageProvider, IJsonSerializer s _serializer = serializer; } - public async ValueTask SetItemAsync(string key, T data) + public async ValueTask SetItemAsync(string key, T data, CancellationToken? cancellationToken = null) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); @@ -27,12 +28,12 @@ public async ValueTask SetItemAsync(string key, T data) return; var serialisedData = _serializer.Serialize(data); - await _storageProvider.SetItemAsync(key, serialisedData).ConfigureAwait(false); + await _storageProvider.SetItemAsync(key, serialisedData, cancellationToken).ConfigureAwait(false); RaiseOnChanged(key, e.OldValue, data); } - public async ValueTask SetItemAsStringAsync(string key, string data) + public async ValueTask SetItemAsStringAsync(string key, string data, CancellationToken? cancellationToken = null) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); @@ -45,17 +46,17 @@ public async ValueTask SetItemAsStringAsync(string key, string data) if (e.Cancel) return; - await _storageProvider.SetItemAsync(key, data).ConfigureAwait(false); + await _storageProvider.SetItemAsync(key, data, cancellationToken).ConfigureAwait(false); RaiseOnChanged(key, e.OldValue, data); } - public async ValueTask GetItemAsync(string key) + public async ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - var serialisedData = await _storageProvider.GetItemAsync(key).ConfigureAwait(false); + var serialisedData = await _storageProvider.GetItemAsync(key, cancellationToken).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(serialisedData)) return default; @@ -72,33 +73,33 @@ public async ValueTask GetItemAsync(string key) } } - public ValueTask GetItemAsStringAsync(string key) + public ValueTask GetItemAsStringAsync(string key, CancellationToken? cancellationToken = null) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - return _storageProvider.GetItemAsync(key); + return _storageProvider.GetItemAsync(key, cancellationToken); } - public ValueTask RemoveItemAsync(string key) + public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); - return _storageProvider.RemoveItemAsync(key); + return _storageProvider.RemoveItemAsync(key, cancellationToken); } - public ValueTask ClearAsync() - => _storageProvider.ClearAsync(); + public ValueTask ClearAsync(CancellationToken? cancellationToken = null) + => _storageProvider.ClearAsync(cancellationToken); - public ValueTask LengthAsync() - => _storageProvider.LengthAsync(); + public ValueTask LengthAsync(CancellationToken? cancellationToken = null) + => _storageProvider.LengthAsync(cancellationToken); - public ValueTask KeyAsync(int index) - => _storageProvider.KeyAsync(index); + public ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null) + => _storageProvider.KeyAsync(index, cancellationToken); - public ValueTask ContainKeyAsync(string key) - => _storageProvider.ContainKeyAsync(key); + public ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null) + => _storageProvider.ContainKeyAsync(key, cancellationToken); public void SetItem(string key, T data) { From 4c1a6c3b26b68a864669b1d3a9bf6b503eceb553 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 23 Nov 2022 15:28:18 -0500 Subject: [PATCH 16/20] - fixes code snippet missing curly braces --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e0f165b..703cebf 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,15 @@ From v4 onwards we use the default the `JsonSerializerOptions` for `System.Text. To retain the old settings use the following configuration when adding Blazored SessionStorage to the DI container: ```csharp -builder.Services.AddBlazoredSessionStorage(config => - config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; - config.JsonSerializerOptions.IgnoreNullValues = true; - config.JsonSerializerOptions.IgnoreReadOnlyProperties = true; - config.JsonSerializerOptions.PropertyNameCaseInsensitive = true; - config.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - config.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; - config.JsonSerializerOptions.WriteIndented = false; +builder.Services.AddBlazoredSessionStorage(config => { + config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; + config.JsonSerializerOptions.IgnoreNullValues = true; + config.JsonSerializerOptions.IgnoreReadOnlyProperties = true; + config.JsonSerializerOptions.PropertyNameCaseInsensitive = true; + config.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + config.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; + config.JsonSerializerOptions.WriteIndented = false; + } ); ``` From e7fe151a74da273c9120b17d5e40742b71723229 Mon Sep 17 00:00:00 2001 From: Q-Sharp <8755388+Q-Sharp@users.noreply.github.com> Date: Wed, 7 Dec 2022 04:50:00 +0100 Subject: [PATCH 17/20] update to dotnet7 + update nuget packages --- .github/workflows/ci-main.yml | 6 +++--- .github/workflows/ci-pr.yml | 2 +- samples/BlazorServer/BlazorServer.csproj | 4 ++-- .../BlazorWebAssembly/BlazorWebAssembly.csproj | 8 ++++---- samples/bUnitExample/bUnitExample.csproj | 12 ++++++------ ...Blazored.SessionStorage.TestExtensions.csproj | 4 ++-- .../Blazored.SessionStorage.csproj | 10 +++++----- .../Blazored.SessionStorage.Tests.csproj | 16 ++++++++-------- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 2287183..b7c42a5 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -7,7 +7,7 @@ on: types: [ published ] env: - NETCORE_VERSION: '5.0.202' + NETCORE_VERSION: '7.0.x' DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true PROJECT_NAME: SessionStorage @@ -73,7 +73,7 @@ jobs: - name: Rewrite base href uses: SteveSandersonMS/ghaction-rewrite-base-href@v1 with: - html_path: samples/BlazorWebAssembly/bin/Release/net5.0/publish/wwwroot/index.html + html_path: samples/BlazorWebAssembly/bin/Release/net7.0/publish/wwwroot/index.html base_href: /${{ env.PROJECT_NAME }}/ - name: Deploy to Github Pages @@ -82,5 +82,5 @@ jobs: ACCESS_TOKEN: $GITHUB_TOKEN BASE_BRANCH: main # The branch the action should deploy from. BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: samples/BlazorWebAssembly/bin/Release/net5.0/publish/wwwroot # The folder the action should deploy. + FOLDER: samples/BlazorWebAssembly/bin/Release/net7.0/publish/wwwroot # The folder the action should deploy. SINGLE_COMMIT: true diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 63c70fd..1584fc7 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -5,7 +5,7 @@ on: branches: [ main ] env: - NETCORE_VERSION: '5.0.x' + NETCORE_VERSION: '7.0.x' PROJECT_NAME: Blazored.SessionStorage jobs: diff --git a/samples/BlazorServer/BlazorServer.csproj b/samples/BlazorServer/BlazorServer.csproj index e6ebf48..98d95a3 100644 --- a/samples/BlazorServer/BlazorServer.csproj +++ b/samples/BlazorServer/BlazorServer.csproj @@ -1,11 +1,11 @@  - net5.0 + net7.0 - + diff --git a/samples/BlazorWebAssembly/BlazorWebAssembly.csproj b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj index 7011596..f1d4e0d 100644 --- a/samples/BlazorWebAssembly/BlazorWebAssembly.csproj +++ b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj @@ -1,13 +1,13 @@  - net5.0 + net7.0 - - - + + + diff --git a/samples/bUnitExample/bUnitExample.csproj b/samples/bUnitExample/bUnitExample.csproj index 2d94f8e..5d0efb7 100644 --- a/samples/bUnitExample/bUnitExample.csproj +++ b/samples/bUnitExample/bUnitExample.csproj @@ -1,18 +1,18 @@ - net5.0 + net7.0 - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj b/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj index f064f63..f75b71b 100644 --- a/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj +++ b/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj @@ -1,7 +1,7 @@  - netstandard2.1;net5.0 + netstandard2.1;net7.0 Chris Sainty @@ -31,7 +31,7 @@ - + diff --git a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj index 194b03e..2ff8b60 100644 --- a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj +++ b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj @@ -1,7 +1,7 @@  - netstandard2.1;net5.0 + netstandard2.1;net7.0 Chris Sainty @@ -30,16 +30,16 @@ 3.0 - + - + - - + + diff --git a/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj b/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj index 085b1c3..00c5d20 100644 --- a/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj +++ b/tests/Blazored.SessionStorage.Tests/Blazored.SessionStorage.Tests.csproj @@ -1,7 +1,7 @@ - net5.0 + net7.0 false @@ -11,16 +11,16 @@ - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 91b9436ea662e5390ccff3427ecdba17904e51cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 15:24:04 +0000 Subject: [PATCH 18/20] Bump Newtonsoft.Json from 13.0.1 to 13.0.2 in /samples/BlazorServer Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 13.0.1 to 13.0.2. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/13.0.1...13.0.2) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- samples/BlazorServer/BlazorServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/BlazorServer/BlazorServer.csproj b/samples/BlazorServer/BlazorServer.csproj index e6ebf48..d3d3719 100644 --- a/samples/BlazorServer/BlazorServer.csproj +++ b/samples/BlazorServer/BlazorServer.csproj @@ -5,7 +5,7 @@ - + From d69ee73ffff6a9e3870d1dedd723ba9085116312 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Sun, 1 Jan 2023 14:29:28 +0000 Subject: [PATCH 19/20] Updated features to match local storage package --- Directory.Build.props | 4 +- README.md | 37 +- ...zored.SessionStorage.TestExtensions.csproj | 8 +- .../InMemoryStorageProvider.cs | 47 +- .../Blazored.SessionStorage.csproj | 18 +- .../BrowserStorageProvider.cs | 344 +++++++++++++-- .../ChangedEventArgs.cs | 15 +- .../ChangingEventArgs.cs | 13 +- .../BrowserStorageDisabledException.cs | 18 + .../ISessionStorageService.cs | 144 +++--- .../IStorageProvider.cs | 39 +- .../ISyncSessionStorageService.cs | 126 +++--- .../JsonConverters/TimespanJsonConverter.cs | 49 +-- .../Serialization/IJsonSerializer.cs | 11 +- .../Serialization/SystemTextJsonSerializer.cs | 35 +- .../ServiceCollectionExtensions.cs | 66 ++- .../SessionStorageService.cs | 412 +++++++++--------- .../StorageOptions/LocalStorageOptions.cs | 9 - .../StorageOptions/SessionStorageOptions.cs | 8 + .../SessionStorageServiceTests/Clear.cs | 73 ++-- .../SessionStorageServiceTests/ClearAsync.cs | 73 ++-- .../SessionStorageServiceTests/ContainsKey.cs | 73 ++-- .../ContainsKeyAsync.cs | 73 ++-- .../SessionStorageServiceTests/GetItem.cs | 151 ++++--- .../GetItemAsString.cs | 109 +++-- .../GetItemAsStringAsync.cs | 109 +++-- .../GetItemAsync.cs | 151 ++++--- .../SessionStorageServiceTests/Key.cs | 81 ++-- .../SessionStorageServiceTests/KeyAsync.cs | 81 ++-- .../SessionStorageServiceTests/Keys.cs | 153 +++++++ .../SessionStorageServiceTests/Length.cs | 77 ++-- .../SessionStorageServiceTests/LengthAsync.cs | 77 ++-- .../SessionStorageServiceTests/RemoveItem.cs | 93 ++-- .../RemoveItemAsync.cs | 93 ++-- .../SessionStorageServiceTests/SetItem.cs | 411 +++++++++-------- .../SetItemAsString.cs | 353 ++++++++------- .../SetItemAsStringAsync.cs | 353 ++++++++------- .../SetItemAsync.cs | 411 +++++++++-------- .../TestAssets/TestObject.cs | 23 +- 39 files changed, 2486 insertions(+), 1935 deletions(-) create mode 100644 src/Blazored.SessionStorage/Exceptions/BrowserStorageDisabledException.cs delete mode 100644 src/Blazored.SessionStorage/StorageOptions/LocalStorageOptions.cs create mode 100644 src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs create mode 100644 tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Keys.cs diff --git a/Directory.Build.props b/Directory.Build.props index 28acef2..8f1e9e9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,8 @@ - 3.1.14 - 5.0.5 + 6.0.12 + 7.0.1 \ No newline at end of file diff --git a/README.md b/README.md index 703cebf..3d1e274 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ builder.Services.AddBlazoredSessionStorage(config => { ``` ### SetItem[Async] method now serializes string values -Prior to v2 we bypassed the serialization of string values as it seemed a pointless as string can be stored directly. However, this led to some edge cases where nullable strings were being saved as the string `"null"`. Then when retrieved, instead of being null the value was `"null"`. By serializing strings this issue is taken care of. +Prior to v2 we bypassed the serialization of string values as it seemed pointless as string can be stored directly. However, this led to some edge cases where nullable strings were being saved as the string `"null"`. Then when retrieved, instead of being null the value was `"null"`. By serializing strings this issue is taken care of. For those who wish to save raw string values, a new method `SetValueAsString[Async]` is available. This will save a string value without attempting to serialize it and will throw an exception if a null string is attempted to be saved. ## Installing @@ -42,7 +42,7 @@ You can also install via the .NET CLI with the following command: dotnet add package Blazored.SessionStorage ``` -If you're using Visual Studio you can also install via the built in NuGet package manager. +If you're using Jetbrains Rider or Visual Studio you can also install via the built in NuGet package manager. ## Setup @@ -69,6 +69,17 @@ public static async Task Main(string[] args) } ``` +### Registering services as Singleton - Blazor WebAssembly **ONLY** +99% of developers will want to register Blazored SessionStorage using the method described above. However, in some very specific scenarios, +developers may have a need to register services as Singleton as apposed to Scoped. This is possible by using the following method: + +```csharp +builder.Services.AddBlazoredSessionStorageAsSingleton(); +``` + +**This method will not work with Blazor Server applications as Blazor's JS interop services are registered as Scoped and cannot be injected into Singletons.** + + ## Usage (Blazor WebAssembly) To use Blazored.SessionStorage in Blazor WebAssembly, inject the `ISessionStorageService` per the example below. @@ -128,9 +139,11 @@ The APIs available are: - GetItemAsync() - GetItemAsStringAsync() - RemoveItemAsync() + - RemoveItemsAsync() - ClearAsync() - LengthAsync() - KeyAsync() + - KeysAsync() - ContainKeyAsync() - synchronous via `ISyncSessionStorageService` (Synchronous methods are **only** available in Blazor WebAssembly): @@ -139,9 +152,11 @@ The APIs available are: - GetItem() - GetItemAsString() - RemoveItem() + - RemovesItem() - Clear() - Length() - Key() + - Keys() - ContainKey() **Note:** Blazored.SessionStorage methods will handle the serialisation and de-serialisation of the data for you, the exception is the `GetItemAsString[Async]` method which will return the raw string value from session storage. @@ -178,6 +193,24 @@ You can find an example of this in the Blazor Server sample project. The standar ## Testing with bUnit This library provides test extensions for use with the [bUnit testing library](https://bunit.dev/). Using these test extensions will provide an in memory implementation which mimics session storage allowing more realistic testing of your components. +### Installing + +To install the package add the following line to you csproj file replacing x.x.x with the latest version number (found at the top of this file): + +``` + +``` + +You can also install via the .NET CLI with the following command: + +``` +dotnet add package Blazored.SessionStorage.TestExtensions +``` + +If you're using Jetbrains Rider or Visual Studio you can also install via the built in NuGet package manager. + +### Usage example + Below is an example test which uses these extensions. You can find an example project which shows this code in action in the samples folder. ```c# diff --git a/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj b/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj index f75b71b..40800be 100644 --- a/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj +++ b/src/Blazored.SessionStorage.TestExtensions/Blazored.SessionStorage.TestExtensions.csproj @@ -1,7 +1,7 @@  - netstandard2.1;net7.0 + net6.0;net7.0 Chris Sainty @@ -26,12 +26,16 @@ Blazored.SessionStorage.snk + + + + - + diff --git a/src/Blazored.SessionStorage.TestExtensions/InMemoryStorageProvider.cs b/src/Blazored.SessionStorage.TestExtensions/InMemoryStorageProvider.cs index 268d1e3..36615c0 100644 --- a/src/Blazored.SessionStorage.TestExtensions/InMemoryStorageProvider.cs +++ b/src/Blazored.SessionStorage.TestExtensions/InMemoryStorageProvider.cs @@ -7,12 +7,12 @@ namespace Blazored.SessionStorage.TestExtensions { internal class InMemoryStorageProvider : IStorageProvider { - private readonly Dictionary _dataStore = new Dictionary(); + private readonly Dictionary _dataStore = new(); public void Clear() => _dataStore.Clear(); - public ValueTask ClearAsync(CancellationToken? cancellationToken = null) + public ValueTask ClearAsync(CancellationToken cancellationToken = default) { _dataStore.Clear(); return new ValueTask(Task.CompletedTask); @@ -21,35 +21,56 @@ public ValueTask ClearAsync(CancellationToken? cancellationToken = null) public bool ContainKey(string key) => _dataStore.ContainsKey(key); - public ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null) - => new ValueTask(ContainKey(key)); + public ValueTask ContainKeyAsync(string key, CancellationToken cancellationToken = default) + => new(ContainKey(key)); public string GetItem(string key) - => _dataStore.ContainsKey(key) ? _dataStore[key] : default; + => _dataStore.TryGetValue(key, out var value) ? value : default; - public ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) - => new ValueTask(GetItem(key)); + public ValueTask GetItemAsync(string key, CancellationToken cancellationToken = default) + => new(GetItem(key)); public string Key(int index) => index > _dataStore.Count - 1 ? default : _dataStore.ElementAt(index).Key; - public ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null) - => new ValueTask(Key(index)); + public IEnumerable Keys() + => _dataStore.Keys.ToList(); + + public ValueTask KeyAsync(int index, CancellationToken cancellationToken = default) + => new(Key(index)); + + public ValueTask> KeysAsync(CancellationToken cancellationToken = default) + => new(_dataStore.Keys.ToList()); public int Length() => _dataStore.Count; - public ValueTask LengthAsync(CancellationToken? cancellationToken = null) - => new ValueTask(Length()); + public ValueTask LengthAsync(CancellationToken cancellationToken = default) + => new(Length()); public void RemoveItem(string key) => _dataStore.Remove(key); - public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) + public ValueTask RemoveItemAsync(string key, CancellationToken cancellationToken = default) { RemoveItem(key); return new ValueTask(Task.CompletedTask); } + + public void RemoveItems(IEnumerable keys) + { + foreach (var key in keys) + { + _dataStore.Remove(key); + } + } + + public ValueTask RemoveItemsAsync(IEnumerable keys, CancellationToken cancellationToken = default) + { + RemoveItems(keys); + + return new ValueTask(Task.CompletedTask); + } public void SetItem(string key, string data) { @@ -63,7 +84,7 @@ public void SetItem(string key, string data) } } - public ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null) + public ValueTask SetItemAsync(string key, string data, CancellationToken cancellationToken = default) { SetItem(key, data); return new ValueTask(Task.CompletedTask); diff --git a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj index 2ff8b60..07ebd5d 100644 --- a/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj +++ b/src/Blazored.SessionStorage/Blazored.SessionStorage.csproj @@ -1,7 +1,7 @@  - netstandard2.1;net7.0 + net6.0;net7.0 Chris Sainty @@ -26,20 +26,16 @@ Blazored.SessionStorage.snk - - 3.0 - - - + - - + + - + - + @@ -62,4 +58,4 @@ - + \ No newline at end of file diff --git a/src/Blazored.SessionStorage/BrowserStorageProvider.cs b/src/Blazored.SessionStorage/BrowserStorageProvider.cs index 6c1e398..883c4bb 100644 --- a/src/Blazored.SessionStorage/BrowserStorageProvider.cs +++ b/src/Blazored.SessionStorage/BrowserStorageProvider.cs @@ -1,88 +1,352 @@ using Microsoft.JSInterop; using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Blazored.SessionStorage.Exceptions; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +internal class BrowserStorageProvider : IStorageProvider { - internal class BrowserStorageProvider : IStorageProvider + private const string StorageNotAvailableMessage = "Unable to access the browser storage. This is most likely due to the browser settings."; + + private readonly IJSRuntime _jSRuntime; + private readonly IJSInProcessRuntime _jSInProcessRuntime; + + public BrowserStorageProvider(IJSRuntime jSRuntime) + { + _jSRuntime = jSRuntime; + _jSInProcessRuntime = jSRuntime as IJSInProcessRuntime; + } + + public async ValueTask ClearAsync(CancellationToken cancellationToken = default) + { + try + { + await _jSRuntime.InvokeVoidAsync("sessionStorage.clear", cancellationToken); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } + + public async ValueTask GetItemAsync(string key, CancellationToken cancellationToken = default) + { + try + { + return await _jSRuntime.InvokeAsync("sessionStorage.getItem", cancellationToken, key); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } + + public async ValueTask KeyAsync(int index, CancellationToken cancellationToken = default) + { + try + { + return await _jSRuntime.InvokeAsync("sessionStorage.key", cancellationToken, index); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } + + public async ValueTask> KeysAsync(CancellationToken cancellationToken = default) { - private readonly IJSRuntime _jSRuntime; - private readonly IJSInProcessRuntime _jSInProcessRuntime; + try + { + return await _jSRuntime.InvokeAsync>("eval", cancellationToken, "Object.keys(sessionStorage)"); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } - public BrowserStorageProvider(IJSRuntime jSRuntime) + public async ValueTask ContainKeyAsync(string key, CancellationToken cancellationToken = default) + { + try { - _jSRuntime = jSRuntime; - _jSInProcessRuntime = jSRuntime as IJSInProcessRuntime; + return await _jSRuntime.InvokeAsync("sessionStorage.hasOwnProperty", cancellationToken, key); } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public ValueTask ClearAsync(CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeVoidAsync("sessionStorage.clear", cancellationToken ?? CancellationToken.None); + throw; + } + } - public ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("sessionStorage.getItem", cancellationToken ?? CancellationToken.None, key); + public async ValueTask LengthAsync(CancellationToken cancellationToken = default) + { + try + { + return await _jSRuntime.InvokeAsync("eval", cancellationToken, "sessionStorage.length"); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("sessionStorage.key", cancellationToken ?? CancellationToken.None, index); + throw; + } + } - public ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("sessionStorage.hasOwnProperty", cancellationToken ?? CancellationToken.None, key); + public async ValueTask RemoveItemAsync(string key, CancellationToken cancellationToken = default) + { + try + { + await _jSRuntime.InvokeVoidAsync("sessionStorage.removeItem", cancellationToken, key); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public ValueTask LengthAsync(CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeAsync("eval", cancellationToken ?? CancellationToken.None, "sessionStorage.length"); + throw; + } + } - public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeVoidAsync("sessionStorage.removeItem", cancellationToken ?? CancellationToken.None, key); + public async ValueTask RemoveItemsAsync(IEnumerable keys, CancellationToken cancellationToken = default) + { + try + { + foreach (var key in keys) + { + await _jSRuntime.InvokeVoidAsync("sessionStorage.removeItem", cancellationToken, key); + } + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null) - => _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", cancellationToken ?? CancellationToken.None, key, data); + throw; + } + } - public void Clear() + public async ValueTask SetItemAsync(string key, string data, CancellationToken cancellationToken = default) + { + try + { + await _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", cancellationToken, key, data); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } + + public void Clear() + { + CheckForInProcessRuntime(); + try { - CheckForInProcessRuntime(); _jSInProcessRuntime.InvokeVoid("sessionStorage.clear"); } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } - public string GetItem(string key) + public string GetItem(string key) + { + CheckForInProcessRuntime(); + try { - CheckForInProcessRuntime(); return _jSInProcessRuntime.Invoke("sessionStorage.getItem", key); } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public string Key(int index) + throw; + } + } + + public string Key(int index) + { + CheckForInProcessRuntime(); + try { - CheckForInProcessRuntime(); return _jSInProcessRuntime.Invoke("sessionStorage.key", index); } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public bool ContainKey(string key) + throw; + } + } + + public IEnumerable Keys() + { + CheckForInProcessRuntime(); + try + { + return _jSInProcessRuntime.Invoke>("eval", "Object.keys(sessionStorage)"); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } + + public bool ContainKey(string key) + { + CheckForInProcessRuntime(); + try { - CheckForInProcessRuntime(); return _jSInProcessRuntime.Invoke("sessionStorage.hasOwnProperty", key); } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public int Length() + throw; + } + } + + public int Length() + { + CheckForInProcessRuntime(); + try { - CheckForInProcessRuntime(); return _jSInProcessRuntime.Invoke("eval", "sessionStorage.length"); } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; + } + } + + public void RemoveItem(string key) + { + CheckForInProcessRuntime(); + try + { + _jSInProcessRuntime.InvokeVoid("sessionStorage.removeItem", key); + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } - public void RemoveItem(string key) + throw; + } + } + + public void RemoveItems(IEnumerable keys) + { + CheckForInProcessRuntime(); + try { - CheckForInProcessRuntime(); - _jSInProcessRuntime.InvokeVoidAsync("sessionStorage.removeItem", key); + foreach (var key in keys) + { + _jSInProcessRuntime.InvokeVoid("sessionStorage.removeItem", key); + } + } + catch (Exception exception) + { + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; } + } - public void SetItem(string key, string data) + public void SetItem(string key, string data) + { + CheckForInProcessRuntime(); + try { - CheckForInProcessRuntime(); _jSInProcessRuntime.InvokeVoid("sessionStorage.setItem", key, data); } - - private void CheckForInProcessRuntime() + catch (Exception exception) { - if (_jSInProcessRuntime == null) - throw new InvalidOperationException("IJSInProcessRuntime not available"); + if (IsStorageDisabledException(exception)) + { + throw new BrowserStorageDisabledException(StorageNotAvailableMessage, exception); + } + + throw; } } + + private void CheckForInProcessRuntime() + { + if (_jSInProcessRuntime == null) + throw new InvalidOperationException("IJSInProcessRuntime not available"); + } + + private static bool IsStorageDisabledException(Exception exception) + => exception.Message.Contains("Failed to read the 'sessionStorage' property from 'Window'"); } diff --git a/src/Blazored.SessionStorage/ChangedEventArgs.cs b/src/Blazored.SessionStorage/ChangedEventArgs.cs index fae2781..f20fba4 100644 --- a/src/Blazored.SessionStorage/ChangedEventArgs.cs +++ b/src/Blazored.SessionStorage/ChangedEventArgs.cs @@ -1,12 +1,11 @@ using System.Diagnostics.CodeAnalysis; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +[ExcludeFromCodeCoverage] +public class ChangedEventArgs { - [ExcludeFromCodeCoverage] - public class ChangedEventArgs - { - public string Key { get; set; } - public object OldValue { get; set; } - public object NewValue { get; set; } - } + public string Key { get; set; } + public object OldValue { get; set; } + public object NewValue { get; set; } } \ No newline at end of file diff --git a/src/Blazored.SessionStorage/ChangingEventArgs.cs b/src/Blazored.SessionStorage/ChangingEventArgs.cs index 047a3e3..032c9e4 100644 --- a/src/Blazored.SessionStorage/ChangingEventArgs.cs +++ b/src/Blazored.SessionStorage/ChangingEventArgs.cs @@ -1,11 +1,10 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +[ExcludeFromCodeCoverage] +public class ChangingEventArgs : ChangedEventArgs { - [ExcludeFromCodeCoverage] - public class ChangingEventArgs : ChangedEventArgs - { - public bool Cancel { get; set; } - } -} + public bool Cancel { get; set; } +} \ No newline at end of file diff --git a/src/Blazored.SessionStorage/Exceptions/BrowserStorageDisabledException.cs b/src/Blazored.SessionStorage/Exceptions/BrowserStorageDisabledException.cs new file mode 100644 index 0000000..212a658 --- /dev/null +++ b/src/Blazored.SessionStorage/Exceptions/BrowserStorageDisabledException.cs @@ -0,0 +1,18 @@ +using System; + +namespace Blazored.SessionStorage.Exceptions; + +public class BrowserStorageDisabledException : Exception +{ + public BrowserStorageDisabledException() + { + } + + public BrowserStorageDisabledException(string message) : base(message) + { + } + + public BrowserStorageDisabledException(string message, Exception inner) : base(message, inner) + { + } +} diff --git a/src/Blazored.SessionStorage/ISessionStorageService.cs b/src/Blazored.SessionStorage/ISessionStorageService.cs index c61c98c..e35d989 100644 --- a/src/Blazored.SessionStorage/ISessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISessionStorageService.cs @@ -1,75 +1,99 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.JSInterop; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +public interface ISessionStorageService { - public interface ISessionStorageService - { - /// - /// Clears all data from session storage. - /// - /// A representing the completion of the operation. - ValueTask ClearAsync(CancellationToken? cancellationToken = null); + /// + /// Clears all data from session storage. + /// + /// A representing the completion of the operation. + ValueTask ClearAsync(CancellationToken cancellationToken = default); - /// - /// Retrieve the specified data from session storage and deseralise it to the specfied type. - /// - /// A value specifying the name of the session storage slot to use - /// A representing the completion of the operation. - ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null); + /// + /// Retrieve the specified data from session storage and deseralise it to the specfied type. + /// + /// A value specifying the name of the session storage slot to use + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask GetItemAsync(string key, CancellationToken cancellationToken = default); - /// - /// Retrieve the specified data from session storage as a . - /// - /// A value specifying the name of the storage slot to use - /// A representing the completion of the operation. - ValueTask GetItemAsStringAsync(string key, CancellationToken? cancellationToken = null); + /// + /// Retrieve the specified data from session storage as a . + /// + /// A value specifying the name of the storage slot to use + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask GetItemAsStringAsync(string key, CancellationToken cancellationToken = default); - /// - /// Return the name of the key at the specified . - /// - /// - /// A representing the completion of the operation. - ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null); + /// + /// Return the name of the key at the specified . + /// + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask KeyAsync(int index, CancellationToken cancellationToken = default); + + /// + /// Returns a collection of strings representing the names of the keys in the Session storage. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask> KeysAsync(CancellationToken cancellationToken = default); - /// - /// Checks if the exists in session storage, but does not check its value. - /// - /// A value specifying the name of the storage slot to use - /// A representing the completion of the operation. - ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null); + /// + /// Checks if the exists in session storage, but does not check its value. + /// + /// A value specifying the name of the storage slot to use + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask ContainKeyAsync(string key, CancellationToken cancellationToken = default); - /// - /// The number of items stored in session storage. - /// - /// A representing the completion of the operation. - ValueTask LengthAsync(CancellationToken? cancellationToken = null); + /// + /// The number of items stored in session storage. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask LengthAsync(CancellationToken cancellationToken = default); - /// - /// Remove the data with the specified . - /// - /// A value specifying the name of the storage slot to use - /// A representing the completion of the operation. - ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null); + /// + /// Remove the data with the specified . + /// + /// A value specifying the name of the storage slot to use + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask RemoveItemAsync(string key, CancellationToken cancellationToken = default); + + /// + /// Removes a collection of . + /// + /// A IEnumerable collection of strings specifying the name of the storage slot to remove + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask RemoveItemsAsync(IEnumerable keys, CancellationToken cancellationToken = default); - /// - /// Sets or updates the in session storage with the specified . - /// - /// A value specifying the name of the storage slot to use - /// The data to be saved - /// A representing the completion of the operation. - ValueTask SetItemAsync(string key, T data, CancellationToken? cancellationToken = null); + /// + /// Sets or updates the in session storage with the specified . + /// + /// A value specifying the name of the storage slot to use + /// The data to be saved + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask SetItemAsync(string key, T data, CancellationToken cancellationToken = default); - /// - /// Sets or updates the in session storage with the specified . Does not serialize the value before storing. - /// - /// A value specifying the name of the storage slot to use - /// The string to be saved - /// - ValueTask SetItemAsStringAsync(string key, string data, CancellationToken? cancellationToken = null); + /// + /// Sets or updates the in session storage with the specified . Does not serialize the value before storing. + /// + /// A value specifying the name of the storage slot to use + /// The string to be saved + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts () from being applied. + /// A representing the completion of the operation. + ValueTask SetItemAsStringAsync(string key, string data, CancellationToken cancellationToken = default); - event EventHandler Changing; - event EventHandler Changed; - } + event EventHandler Changing; + event EventHandler Changed; } diff --git a/src/Blazored.SessionStorage/IStorageProvider.cs b/src/Blazored.SessionStorage/IStorageProvider.cs index df476de..0bd2c7f 100644 --- a/src/Blazored.SessionStorage/IStorageProvider.cs +++ b/src/Blazored.SessionStorage/IStorageProvider.cs @@ -1,23 +1,26 @@ +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +internal interface IStorageProvider { - internal interface IStorageProvider - { - void Clear(); - ValueTask ClearAsync(CancellationToken? cancellationToken = null); - bool ContainKey(string key); - ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null); - string GetItem(string key); - ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null); - string Key(int index); - ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null); - int Length(); - ValueTask LengthAsync(CancellationToken? cancellationToken = null); - void RemoveItem(string key); - ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null); - void SetItem(string key, string data); - ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null); - } + void Clear(); + ValueTask ClearAsync(CancellationToken cancellationToken = default); + bool ContainKey(string key); + ValueTask ContainKeyAsync(string key, CancellationToken cancellationToken = default); + string GetItem(string key); + ValueTask GetItemAsync(string key, CancellationToken cancellationToken = default); + string Key(int index); + ValueTask KeyAsync(int index, CancellationToken cancellationToken = default); + IEnumerable Keys(); + ValueTask> KeysAsync(CancellationToken cancellationToken = default); + int Length(); + ValueTask LengthAsync(CancellationToken cancellationToken = default); + void RemoveItem(string key); + ValueTask RemoveItemAsync(string key, CancellationToken cancellationToken = default); + ValueTask RemoveItemsAsync(IEnumerable keys, CancellationToken cancellationToken = default); + void SetItem(string key, string data); + ValueTask SetItemAsync(string key, string data, CancellationToken cancellationToken = default); } diff --git a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs index 0017e89..d972a1a 100644 --- a/src/Blazored.SessionStorage/ISyncSessionStorageService.cs +++ b/src/Blazored.SessionStorage/ISyncSessionStorageService.cs @@ -1,70 +1,82 @@ using System; +using System.Collections.Generic; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +public interface ISyncSessionStorageService { - public interface ISyncSessionStorageService - { - /// - /// Clears all data from session storage. - /// - void Clear(); + /// + /// Clears all data from session storage. + /// + void Clear(); - /// - /// Retrieve the specified data from session storage as a . - /// - /// A value specifying the name of the session storage slot to use - /// The data from the specified as a - T GetItem(string key); + /// + /// Retrieve the specified data from session storage as a . + /// + /// A value specifying the name of the session storage slot to use + /// The data from the specified as a + T GetItem(string key); - /// - /// Retrieve the specified data from session storage as a . - /// - /// A value specifying the name of the storage slot to use - /// The data associated with the specified as a - string GetItemAsString(string key); + /// + /// Retrieve the specified data from session storage as a . + /// + /// A value specifying the name of the storage slot to use + /// The data associated with the specified as a + string GetItemAsString(string key); - /// - /// Return the name of the key at the specified . - /// - /// - /// The name of the key at the specified - string Key(int index); + /// + /// Return the name of the key at the specified . + /// + /// + /// The name of the key at the specified + string Key(int index); + + /// + /// Get the keys of all items stored in session storage. + /// + /// The keys of all items stored in session storage + IEnumerable Keys(); - /// - /// Checks if the exists in session storage, but does not check its value. - /// - /// A value specifying the name of the storage slot to use - /// Boolean indicating if the specified exists - bool ContainKey(string key); + /// + /// Checks if the exists in session storage, but does not check its value. + /// + /// A value specifying the name of the storage slot to use + /// Boolean indicating if the specified exists + bool ContainKey(string key); - /// - /// The number of items stored in session storage. - /// - /// The number of items stored in session storage - int Length(); + /// + /// The number of items stored in session storage. + /// + /// The number of items stored in session storage + int Length(); - /// - /// Remove the data with the specified . - /// - /// A value specifying the name of the storage slot to use - void RemoveItem(string key); + /// + /// Remove the data with the specified . + /// + /// A value specifying the name of the storage slot to use + void RemoveItem(string key); + + /// + /// Removes a collection of . + /// + /// A IEnumerable collection of strings specifying the name of the storage slot to remove + void RemoveItems(IEnumerable keys); - /// - /// Sets or updates the in session storage with the specified . - /// - /// A value specifying the name of the storage slot to use - /// The data to be saved - void SetItem(string key, T data); + /// + /// Sets or updates the in session storage with the specified . + /// + /// A value specifying the name of the storage slot to use + /// The data to be saved + void SetItem(string key, T data); - /// - /// Sets or updates the in session storage with the specified . Does not serialize the value before storing. - /// - /// A value specifying the name of the storage slot to use - /// The string to be saved - /// - void SetItemAsString(string key, string data); + /// + /// Sets or updates the in session storage with the specified . Does not serialize the value before storing. + /// + /// A value specifying the name of the storage slot to use + /// The string to be saved + /// + void SetItemAsString(string key, string data); - event EventHandler Changing; - event EventHandler Changed; - } + event EventHandler Changing; + event EventHandler Changed; } diff --git a/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs b/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs index b67e0a8..2a81538 100644 --- a/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs +++ b/src/Blazored.SessionStorage/JsonConverters/TimespanJsonConverter.cs @@ -5,39 +5,38 @@ using System.Text.Json.Serialization; using System.Text.RegularExpressions; -namespace Blazored.SessionStorage.JsonConverters +namespace Blazored.SessionStorage.JsonConverters; + +/// +/// The new Json.NET doesn't support Timespan at this time +/// https://github.com/dotnet/corefx/issues/38641 +/// +public class TimespanJsonConverter : JsonConverter { /// - /// The new Json.NET doesn't support Timespan at this time - /// https://github.com/dotnet/corefx/issues/38641 + /// Format: Days.Hours:Minutes:Seconds:Milliseconds /// - public class TimespanJsonConverter : JsonConverter - { - /// - /// Format: Days.Hours:Minutes:Seconds:Milliseconds - /// - public const string TimeSpanFormatString = @"d\.hh\:mm\:ss\:FFF"; + public const string TimeSpanFormatString = @"d\.hh\:mm\:ss\:FFF"; - public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var s = reader.GetString(); + if (string.IsNullOrWhiteSpace(s)) { - var s = reader.GetString(); - if (string.IsNullOrWhiteSpace(s)) - { - return TimeSpan.Zero; - } - - if (!TimeSpan.TryParseExact(s, TimeSpanFormatString, null, out var parsedTimeSpan)) - { - throw new FormatException($"Input timespan is not in an expected format : expected {Regex.Unescape(TimeSpanFormatString)}. Please retrieve this key as a string and parse manually."); - } - - return parsedTimeSpan; + return TimeSpan.Zero; } - public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + if (!TimeSpan.TryParseExact(s, TimeSpanFormatString, null, out var parsedTimeSpan)) { - var timespanFormatted = $"{value.ToString(TimeSpanFormatString)}"; - writer.WriteStringValue(timespanFormatted); + throw new FormatException($"Input timespan is not in an expected format : expected {Regex.Unescape(TimeSpanFormatString)}. Please retrieve this key as a string and parse manually."); } + + return parsedTimeSpan; + } + + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + { + var timespanFormatted = $"{value.ToString(TimeSpanFormatString)}"; + writer.WriteStringValue(timespanFormatted); } } diff --git a/src/Blazored.SessionStorage/Serialization/IJsonSerializer.cs b/src/Blazored.SessionStorage/Serialization/IJsonSerializer.cs index 9bccf18..e1a99c5 100644 --- a/src/Blazored.SessionStorage/Serialization/IJsonSerializer.cs +++ b/src/Blazored.SessionStorage/Serialization/IJsonSerializer.cs @@ -1,8 +1,7 @@ -namespace Blazored.SessionStorage.Serialization +namespace Blazored.SessionStorage.Serialization; + +public interface IJsonSerializer { - public interface IJsonSerializer - { - string Serialize(T obj); - T Deserialize(string text); - } + string Serialize(T obj); + T Deserialize(string text); } diff --git a/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs b/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs index 5ce56c0..a4b893f 100644 --- a/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs +++ b/src/Blazored.SessionStorage/Serialization/SystemTextJsonSerializer.cs @@ -2,26 +2,25 @@ using Microsoft.Extensions.Options; using System.Text.Json; -namespace Blazored.SessionStorage.Serialization +namespace Blazored.SessionStorage.Serialization; + +internal class SystemTextJsonSerializer : IJsonSerializer { - internal class SystemTextJsonSerializer : IJsonSerializer - { - private readonly JsonSerializerOptions _options; + private readonly JsonSerializerOptions _options; - public SystemTextJsonSerializer(IOptions options) - { - _options = options.Value.JsonSerializerOptions; - } + public SystemTextJsonSerializer(IOptions options) + { + _options = options.Value.JsonSerializerOptions; + } - public SystemTextJsonSerializer(SessionStorageOptions sessionStorageOptions) - { - _options = sessionStorageOptions.JsonSerializerOptions; - } + public SystemTextJsonSerializer(SessionStorageOptions sessionStorageOptions) + { + _options = sessionStorageOptions.JsonSerializerOptions; + } - public T Deserialize(string data) - => JsonSerializer.Deserialize(data, _options); + public T Deserialize(string data) + => JsonSerializer.Deserialize(data, _options); - public string Serialize(T data) - => JsonSerializer.Serialize(data, _options); - } -} + public string Serialize(T data) + => JsonSerializer.Serialize(data, _options); +} \ No newline at end of file diff --git a/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs b/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs index 9b1e2bf..795a92c 100644 --- a/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs +++ b/src/Blazored.SessionStorage/ServiceCollectionExtensions.cs @@ -5,26 +5,54 @@ using Blazored.SessionStorage.StorageOptions; using Microsoft.Extensions.DependencyInjection; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +[ExcludeFromCodeCoverage] +public static class ServiceCollectionExtensions { - [ExcludeFromCodeCoverage] - public static class ServiceCollectionExtensions - { - public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services) - => AddBlazoredSessionStorage(services, null); + public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services) + => AddBlazoredSessionStorage(services, null); - public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services, Action configure) - { - return services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .Configure(configureOptions => - { - configure?.Invoke(configureOptions); - configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); - }); - } + public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services, Action configure) + { + return services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .Configure(configureOptions => + { + configure?.Invoke(configureOptions); + configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); + }); + } + + /// + /// Registers the Blazored SessionStorage services as singletons. This should only be used in Blazor WebAssembly applications. + /// Using this in Blazor Server applications will cause unexpected and potentially dangerous behaviour. + /// + /// + public static IServiceCollection AddBlazoredSessionStorageAsSingleton(this IServiceCollection services) + => AddBlazoredSessionStorageAsSingleton(services, null); + + /// + /// Registers the Blazored SessionStorage services as singletons. This should only be used in Blazor WebAssembly applications. + /// Using this in Blazor Server applications will cause unexpected and potentially dangerous behaviour. + /// + /// + /// + /// + public static IServiceCollection AddBlazoredSessionStorageAsSingleton(this IServiceCollection services, Action configure) + { + return services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .Configure(configureOptions => + { + configure?.Invoke(configureOptions); + configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter()); + }); } } diff --git a/src/Blazored.SessionStorage/SessionStorageService.cs b/src/Blazored.SessionStorage/SessionStorageService.cs index d020994..c9b429d 100644 --- a/src/Blazored.SessionStorage/SessionStorageService.cs +++ b/src/Blazored.SessionStorage/SessionStorageService.cs @@ -1,269 +1,289 @@ using System; +using System.Collections.Generic; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Blazored.SessionStorage.Serialization; -namespace Blazored.SessionStorage +namespace Blazored.SessionStorage; + +internal class SessionStorageService : ISessionStorageService, ISyncSessionStorageService { - internal class SessionStorageService : ISessionStorageService, ISyncSessionStorageService + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + public SessionStorageService(IStorageProvider storageProvider, IJsonSerializer serializer) { - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; + _storageProvider = storageProvider; + _serializer = serializer; + } - public SessionStorageService(IStorageProvider storageProvider, IJsonSerializer serializer) - { - _storageProvider = storageProvider; - _serializer = serializer; - } + public ValueTask RemoveItemsAsync(IEnumerable keys, CancellationToken cancellationToken = default) + => _storageProvider.RemoveItemsAsync(keys, cancellationToken); - public async ValueTask SetItemAsync(string key, T data, CancellationToken? cancellationToken = null) - { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); + public async ValueTask SetItemAsync(string key, T data, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - var e = await RaiseOnChangingAsync(key, data).ConfigureAwait(false); + var e = await RaiseOnChangingAsync(key, data).ConfigureAwait(false); - if (e.Cancel) - return; + if (e.Cancel) + return; - var serialisedData = _serializer.Serialize(data); - await _storageProvider.SetItemAsync(key, serialisedData, cancellationToken).ConfigureAwait(false); + var serialisedData = _serializer.Serialize(data); + await _storageProvider.SetItemAsync(key, serialisedData, cancellationToken).ConfigureAwait(false); - RaiseOnChanged(key, e.OldValue, data); - } + RaiseOnChanged(key, e.OldValue, data); + } - public async ValueTask SetItemAsStringAsync(string key, string data, CancellationToken? cancellationToken = null) - { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); + public async ValueTask SetItemAsStringAsync(string key, string data, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - if (data is null) - throw new ArgumentNullException(nameof(data)); + if (data is null) + throw new ArgumentNullException(nameof(data)); - var e = await RaiseOnChangingAsync(key, data).ConfigureAwait(false); + var e = await RaiseOnChangingAsync(key, data).ConfigureAwait(false); - if (e.Cancel) - return; + if (e.Cancel) + return; - await _storageProvider.SetItemAsync(key, data, cancellationToken).ConfigureAwait(false); + await _storageProvider.SetItemAsync(key, data, cancellationToken).ConfigureAwait(false); - RaiseOnChanged(key, e.OldValue, data); - } + RaiseOnChanged(key, e.OldValue, data); + } + + public async ValueTask GetItemAsync(string key, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - public async ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) + var serialisedData = await _storageProvider.GetItemAsync(key, cancellationToken).ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(serialisedData)) + return default; + + try { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); - - var serialisedData = await _storageProvider.GetItemAsync(key, cancellationToken).ConfigureAwait(false); - - if (string.IsNullOrWhiteSpace(serialisedData)) - return default; - - try - { - return _serializer.Deserialize(serialisedData); - } - catch (JsonException e) when (e.Path == "$" && typeof(T) == typeof(string)) - { - // For backward compatibility return the plain string. - // On the next save a correct value will be stored and this Exception will not happen again, for this 'key' - return (T)(object)serialisedData; - } + return _serializer.Deserialize(serialisedData); } - - public ValueTask GetItemAsStringAsync(string key, CancellationToken? cancellationToken = null) + catch (JsonException e) when (e.Path == "$" && typeof(T) == typeof(string)) { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); - - return _storageProvider.GetItemAsync(key, cancellationToken); + // For backward compatibility return the plain string. + // On the next save a correct value will be stored and this Exception will not happen again, for this 'key' + return (T)(object)serialisedData; } + } - public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) - { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); + public ValueTask GetItemAsStringAsync(string key, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - return _storageProvider.RemoveItemAsync(key, cancellationToken); - } + return _storageProvider.GetItemAsync(key, cancellationToken); + } - public ValueTask ClearAsync(CancellationToken? cancellationToken = null) - => _storageProvider.ClearAsync(cancellationToken); + public ValueTask RemoveItemAsync(string key, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - public ValueTask LengthAsync(CancellationToken? cancellationToken = null) - => _storageProvider.LengthAsync(cancellationToken); + return _storageProvider.RemoveItemAsync(key, cancellationToken); + } - public ValueTask KeyAsync(int index, CancellationToken? cancellationToken = null) - => _storageProvider.KeyAsync(index, cancellationToken); + public ValueTask ClearAsync(CancellationToken cancellationToken = default) + => _storageProvider.ClearAsync(cancellationToken); - public ValueTask ContainKeyAsync(string key, CancellationToken? cancellationToken = null) - => _storageProvider.ContainKeyAsync(key, cancellationToken); + public ValueTask LengthAsync(CancellationToken cancellationToken = default) + => _storageProvider.LengthAsync(cancellationToken); - public void SetItem(string key, T data) - { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); + public ValueTask KeyAsync(int index, CancellationToken cancellationToken = default) + => _storageProvider.KeyAsync(index, cancellationToken); - var e = RaiseOnChangingSync(key, data); + public ValueTask> KeysAsync(CancellationToken cancellationToken = default) + => _storageProvider.KeysAsync(cancellationToken); - if (e.Cancel) - return; + public ValueTask ContainKeyAsync(string key, CancellationToken cancellationToken = default) + => _storageProvider.ContainKeyAsync(key, cancellationToken); - var serialisedData = _serializer.Serialize(data); - _storageProvider.SetItem(key, serialisedData); + public void SetItem(string key, T data) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - RaiseOnChanged(key, e.OldValue, data); - } + var e = RaiseOnChangingSync(key, data); - public void SetItemAsString(string key, string data) - { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); + if (e.Cancel) + return; - if (data is null) - throw new ArgumentNullException(nameof(data)); + var serialisedData = _serializer.Serialize(data); + _storageProvider.SetItem(key, serialisedData); - var e = RaiseOnChangingSync(key, data); + RaiseOnChanged(key, e.OldValue, data); + } - if (e.Cancel) - return; + public void SetItemAsString(string key, string data) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - _storageProvider.SetItem(key, data); + if (data is null) + throw new ArgumentNullException(nameof(data)); - RaiseOnChanged(key, e.OldValue, data); - } + var e = RaiseOnChangingSync(key, data); + + if (e.Cancel) + return; + + _storageProvider.SetItem(key, data); + + RaiseOnChanged(key, e.OldValue, data); + } + + public T GetItem(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); - public T GetItem(string key) + var serialisedData = _storageProvider.GetItem(key); + + if (string.IsNullOrWhiteSpace(serialisedData)) + return default; + + try { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); - - var serialisedData = _storageProvider.GetItem(key); - - if (string.IsNullOrWhiteSpace(serialisedData)) - return default; - - try - { - return _serializer.Deserialize(serialisedData); - } - catch (JsonException e) when (e.Path == "$" && typeof(T) == typeof(string)) - { - // For backward compatibility return the plain string. - // On the next save a correct value will be stored and this Exception will not happen again, for this 'key' - return (T)(object)serialisedData; - } + return _serializer.Deserialize(serialisedData); } - - public string GetItemAsString(string key) + catch (JsonException e) when (e.Path == "$" && typeof(T) == typeof(string)) { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); - - return _storageProvider.GetItem(key); + // For backward compatibility return the plain string. + // On the next save a correct value will be stored and this Exception will not happen again, for this 'key' + return (T)(object)serialisedData; } + } - public void RemoveItem(string key) - { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); + public string GetItemAsString(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); + return _storageProvider.GetItem(key); + } + + public void RemoveItem(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentNullException(nameof(key)); + + _storageProvider.RemoveItem(key); + } + + public void RemoveItems(IEnumerable keys) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + + foreach (var key in keys) + { _storageProvider.RemoveItem(key); } + } - public void Clear() - => _storageProvider.Clear(); + public void Clear() + => _storageProvider.Clear(); - public int Length() - => _storageProvider.Length(); + public int Length() + => _storageProvider.Length(); - public string Key(int index) - => _storageProvider.Key(index); + public string Key(int index) + => _storageProvider.Key(index); + + public IEnumerable Keys() + => _storageProvider.Keys(); - public bool ContainKey(string key) - => _storageProvider.ContainKey(key); + public bool ContainKey(string key) + => _storageProvider.ContainKey(key); - public event EventHandler Changing; - private async Task RaiseOnChangingAsync(string key, object data) + public event EventHandler Changing; + private async Task RaiseOnChangingAsync(string key, object data) + { + var e = new ChangingEventArgs { - var e = new ChangingEventArgs - { - Key = key, - OldValue = await GetItemInternalAsync(key).ConfigureAwait(false), - NewValue = data - }; + Key = key, + OldValue = await GetItemInternalAsync(key).ConfigureAwait(false), + NewValue = data + }; - Changing?.Invoke(this, e); + Changing?.Invoke(this, e); - return e; - } + return e; + } - private ChangingEventArgs RaiseOnChangingSync(string key, object data) + private ChangingEventArgs RaiseOnChangingSync(string key, object data) + { + var e = new ChangingEventArgs { - var e = new ChangingEventArgs - { - Key = key, - OldValue = GetItemInternal(key), - NewValue = data - }; + Key = key, + OldValue = GetItemInternal(key), + NewValue = data + }; - Changing?.Invoke(this, e); + Changing?.Invoke(this, e); - return e; - } + return e; + } + + private async Task GetItemInternalAsync(string key) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); - private async Task GetItemInternalAsync(string key) + var serialisedData = await _storageProvider.GetItemAsync(key).ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(serialisedData)) + return default; + try { - if (string.IsNullOrEmpty(key)) - throw new ArgumentNullException(nameof(key)); - - var serialisedData = await _storageProvider.GetItemAsync(key).ConfigureAwait(false); - - if (string.IsNullOrWhiteSpace(serialisedData)) - return default; - try - { - return _serializer.Deserialize(serialisedData); - } - catch (JsonException) - { - return (T)(object)serialisedData; - } + return _serializer.Deserialize(serialisedData); } - - private object GetItemInternal(string key) + catch (JsonException) { - if (string.IsNullOrEmpty(key)) - throw new ArgumentNullException(nameof(key)); - - var serialisedData = _storageProvider.GetItem(key); - - if (string.IsNullOrWhiteSpace(serialisedData)) - return default; - - try - { - return _serializer.Deserialize(serialisedData); - } - catch (JsonException) - { - return serialisedData; - } + return (T)(object)serialisedData; } + } + + private object GetItemInternal(string key) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); - public event EventHandler Changed; - private void RaiseOnChanged(string key, object oldValue, object data) + var serialisedData = _storageProvider.GetItem(key); + + if (string.IsNullOrWhiteSpace(serialisedData)) + return default; + + try { - var e = new ChangedEventArgs - { - Key = key, - OldValue = oldValue, - NewValue = data - }; - - Changed?.Invoke(this, e); + return _serializer.Deserialize(serialisedData); } + catch (JsonException) + { + return serialisedData; + } + } + + public event EventHandler Changed; + private void RaiseOnChanged(string key, object oldValue, object data) + { + var e = new ChangedEventArgs + { + Key = key, + OldValue = oldValue, + NewValue = data + }; + + Changed?.Invoke(this, e); } } diff --git a/src/Blazored.SessionStorage/StorageOptions/LocalStorageOptions.cs b/src/Blazored.SessionStorage/StorageOptions/LocalStorageOptions.cs deleted file mode 100644 index 509f1f0..0000000 --- a/src/Blazored.SessionStorage/StorageOptions/LocalStorageOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json; - -namespace Blazored.SessionStorage.StorageOptions -{ - public class SessionStorageOptions - { - public JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions(); - } -} diff --git a/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs b/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs new file mode 100644 index 0000000..2156750 --- /dev/null +++ b/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs @@ -0,0 +1,8 @@ +using System.Text.Json; + +namespace Blazored.SessionStorage.StorageOptions; + +public class SessionStorageOptions +{ + public JsonSerializerOptions JsonSerializerOptions { get; } = new(); +} diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs index a7c8331..4b68577 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Clear.cs @@ -8,49 +8,48 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class Clear { - public class Clear - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; - public Clear() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, serializer); - } + public Clear() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } - [Fact] - public void ClearsAnyItemsInTheStore() - { - // Arrange - var item1 = new TestObject(1, "Jane Smith"); - var item2 = new TestObject(2, "John Smith"); + [Fact] + public void ClearsAnyItemsInTheStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); - _sut.SetItem("Item1", item1); - _sut.SetItem("Item2", item2); + _sut.SetItem("Item1", item1); + _sut.SetItem("Item2", item2); - // Act - _sut.Clear(); + // Act + _sut.Clear(); - // Assert - Assert.Equal(0, _storageProvider.Length()); - } + // Assert + Assert.Equal(0, _storageProvider.Length()); + } - [Fact] - public void DoesNothingWhenStoreIsEmpty() - { - // Act - _sut.Clear(); + [Fact] + public void DoesNothingWhenStoreIsEmpty() + { + // Act + _sut.Clear(); - // Assert - Assert.Equal(0, _storageProvider.Length()); - } + // Assert + Assert.Equal(0, _storageProvider.Length()); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs index 69a71e4..3c39ac9 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ClearAsync.cs @@ -9,49 +9,48 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class ClearAsync { - public class ClearAsync - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; - public ClearAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, serializer); - } + public ClearAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } - [Fact] - public async Task ClearsAnyItemsInTheStore() - { - // Arrange - var item1 = new TestObject(1, "Jane Smith"); - var item2 = new TestObject(2, "John Smith"); + [Fact] + public async Task ClearsAnyItemsInTheStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); - await _sut.SetItemAsync("Item1", item1); - await _sut.SetItemAsync("Item2", item2); + await _sut.SetItemAsync("Item1", item1); + await _sut.SetItemAsync("Item2", item2); - // Act - await _sut.ClearAsync(); + // Act + await _sut.ClearAsync(); - // Assert - Assert.Equal(0, await _storageProvider.LengthAsync()); - } + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } - [Fact] - public async Task DoesNothingWhenItemDoesNotExistInStore() - { - // Act - await _sut.ClearAsync(); + [Fact] + public async Task DoesNothingWhenItemDoesNotExistInStore() + { + // Act + await _sut.ClearAsync(); - // Assert - Assert.Equal(0, await _storageProvider.LengthAsync()); - } + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs index b09f482..81b67ab 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKey.cs @@ -8,49 +8,48 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class ContainsKey { - public class ContainsKey - { - private readonly SessionStorageService _sut; + private readonly SessionStorageService _sut; - public ContainsKey() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - IStorageProvider storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(storageProvider, serializer); - } + public ContainsKey() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } - [Fact] - public void ReturnsTrue_When_KeyExistsInStore() - { - // Arrange - const string key = "TestKey"; - var item1 = new TestObject(1, "Jane Smith"); - _sut.SetItem(key, item1); + [Fact] + public void ReturnsTrue_When_KeyExistsInStore() + { + // Arrange + const string key = "TestKey"; + var item1 = new TestObject(1, "Jane Smith"); + _sut.SetItem(key, item1); - // Act - var containsKey = _sut.ContainKey(key); + // Act + var containsKey = _sut.ContainKey(key); - // Assert - Assert.True(containsKey); - } + // Assert + Assert.True(containsKey); + } - [Fact] - public void ReturnsFalse_When_KeyDoesNotExistsInStore() - { - // Arrange - const string key = "TestKey"; + [Fact] + public void ReturnsFalse_When_KeyDoesNotExistsInStore() + { + // Arrange + const string key = "TestKey"; - // Act - var containsKey = _sut.ContainKey(key); + // Act + var containsKey = _sut.ContainKey(key); - // Assert - Assert.False(containsKey); - } + // Assert + Assert.False(containsKey); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs index c2a9cc4..74c65de 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/ContainsKeyAsync.cs @@ -9,49 +9,48 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class ContainsKeyAsync { - public class ContainsKeyAsync - { - private readonly SessionStorageService _sut; + private readonly SessionStorageService _sut; - public ContainsKeyAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - IStorageProvider storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(storageProvider, serializer); - } + public ContainsKeyAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } - [Fact] - public async Task ReturnsTrue_When_KeyExistsInStore() - { - // Arrange - const string key = "TestKey"; - var item1 = new TestObject(1, "Jane Smith"); - await _sut.SetItemAsync(key, item1); + [Fact] + public async Task ReturnsTrue_When_KeyExistsInStore() + { + // Arrange + const string key = "TestKey"; + var item1 = new TestObject(1, "Jane Smith"); + await _sut.SetItemAsync(key, item1); - // Act - var containsKey = await _sut.ContainKeyAsync(key); + // Act + var containsKey = await _sut.ContainKeyAsync(key); - // Assert - Assert.True(containsKey); - } + // Assert + Assert.True(containsKey); + } - [Fact] - public async Task ReturnsFalse_When_KeyDoesNotExistsInStore() - { - // Arrange - const string key = "TestKey"; + [Fact] + public async Task ReturnsFalse_When_KeyDoesNotExistsInStore() + { + // Arrange + const string key = "TestKey"; - // Act - var containsKey = await _sut.ContainKeyAsync(key); + // Act + var containsKey = await _sut.ContainKeyAsync(key); - // Assert - Assert.False(containsKey); - } + // Assert + Assert.False(containsKey); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs index 6052294..ee957a0 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItem.cs @@ -9,97 +9,96 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class GetItem { - public class GetItem - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; - private const string Key = "testKey"; + private const string Key = "testKey"; - public GetItem() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } + public GetItem() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - var action = new Action(() => _sut.GetItem(key)); + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Action(() => _sut.GetItem(key)); - // assert - Assert.Throws(action); - } + // assert + Assert.Throws(action); + } - [Theory] - [InlineData("Item1", "stringTest")] - [InlineData("Item2", 11)] - [InlineData("Item3", 11.11)] - public void ReturnsDeserializedDataFromStore(string key, T data) - { - // Arrange - _sut.SetItem(key, data); + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public void ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + _sut.SetItem(key, data); - // Act - var result = _sut.GetItem(key); + // Act + var result = _sut.GetItem(key); - // Assert - Assert.Equal(data, result); - } + // Assert + Assert.Equal(data, result); + } - [Fact] - public void ReturnsComplexObjectFromStore() - { - // Arrange - var objectToSave = new TestObject(2, "Jane Smith"); - _sut.SetItem(Key, objectToSave); + [Fact] + public void ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + _sut.SetItem(Key, objectToSave); - // Act - var result = _sut.GetItem(Key); + // Act + var result = _sut.GetItem(Key); - // Assert - Assert.Equal(objectToSave.Id, result.Id); - Assert.Equal(objectToSave.Name, result.Name); - } + // Assert + Assert.Equal(objectToSave.Id, result.Id); + Assert.Equal(objectToSave.Name, result.Name); + } - [Fact] - public void ReturnsNullFromStore_When_NullValueSaved() - { - // Arrange - var valueToSave = (string)null; - _sut.SetItem(Key, valueToSave); + [Fact] + public void ReturnsNullFromStore_When_NullValueSaved() + { + // Arrange + var valueToSave = (string)null; + _sut.SetItem(Key, valueToSave); - // Act - var result = _sut.GetItem(Key); + // Act + var result = _sut.GetItem(Key); - // Assert - Assert.Null(result); - } + // Assert + Assert.Null(result); + } - [Fact] - public void ReturnsStringFromStore_When_JsonExceptionIsThrown() - { - // Arrange - var jsonData = "[{ id: 5, name: \"Jane Smith\"}]"; - _storageProvider.SetItem(Key, jsonData); + [Fact] + public void ReturnsStringFromStore_When_JsonExceptionIsThrown() + { + // Arrange + var jsonData = "[{ id: 5, name: \"Jane Smith\"}]"; + _storageProvider.SetItem(Key, jsonData); - // Act - var result = _sut.GetItem(Key); + // Act + var result = _sut.GetItem(Key); - // Assert - Assert.Equal(jsonData, result); - } + // Assert + Assert.Equal(jsonData, result); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs index 253ed78..92f985a 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsString.cs @@ -9,70 +9,69 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class GetItemAsString { - public class GetItemAsString - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; - private const string Key = "testKey"; + private const string Key = "testKey"; - public GetItemAsString() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } + public GetItemAsString() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - var action = new Action(() => _sut.GetItemAsString(key)); + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Action(() => _sut.GetItemAsString(key)); - // assert - Assert.Throws(action); - } + // assert + Assert.Throws(action); + } - [Theory] - [InlineData("Item1", "stringTest")] - [InlineData("Item2", 11)] - [InlineData("Item3", 11.11)] - public void ReturnsDeserializedDataFromStore(string key, T data) - { - // Arrange - _sut.SetItem(key, data); + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public void ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + _sut.SetItem(key, data); - // Act - var result = _sut.GetItemAsString(key); + // Act + var result = _sut.GetItemAsString(key); - // Assert - var serializedData = _serializer.Serialize(data); - Assert.Equal(serializedData, result); - } + // Assert + var serializedData = _serializer.Serialize(data); + Assert.Equal(serializedData, result); + } - [Fact] - public void ReturnsComplexObjectFromStore() - { - // Arrange - var objectToSave = new TestObject(2, "Jane Smith"); - _sut.SetItem(Key, objectToSave); + [Fact] + public void ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + _sut.SetItem(Key, objectToSave); - // Act - var result = _sut.GetItemAsString(Key); + // Act + var result = _sut.GetItemAsString(Key); - // Assert - var serializedData = _serializer.Serialize(objectToSave); - Assert.Equal(serializedData, result); - } + // Assert + var serializedData = _serializer.Serialize(objectToSave); + Assert.Equal(serializedData, result); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs index 9a7b3d7..a3dedb7 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsStringAsync.cs @@ -10,70 +10,69 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class GetItemAsStringAsync { - public class GetItemAsStringAsync - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; - private const string Key = "testKey"; + private const string Key = "testKey"; - public GetItemAsStringAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } + public GetItemAsStringAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - var action = new Func(async () => await _sut.GetItemAsStringAsync(key)); + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Func(async () => await _sut.GetItemAsStringAsync(key)); - // assert - Assert.ThrowsAsync(action); - } + // assert + Assert.ThrowsAsync(action); + } - [Theory] - [InlineData("Item1", "stringTest")] - [InlineData("Item2", 11)] - [InlineData("Item3", 11.11)] - public async Task ReturnsDeserializedDataFromStore(string key, T data) - { - // Arrange - await _sut.SetItemAsync(key, data); + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public async Task ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + await _sut.SetItemAsync(key, data); - // Act - var result = await _sut.GetItemAsStringAsync(key); + // Act + var result = await _sut.GetItemAsStringAsync(key); - // Assert - var serializedData = _serializer.Serialize(data); - Assert.Equal(serializedData, result); - } + // Assert + var serializedData = _serializer.Serialize(data); + Assert.Equal(serializedData, result); + } - [Fact] - public async Task ReturnsComplexObjectFromStore() - { - // Arrange - var objectToSave = new TestObject(2, "Jane Smith"); - await _sut.SetItemAsync(Key, objectToSave); + [Fact] + public async Task ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + await _sut.SetItemAsync(Key, objectToSave); - // Act - var result = await _sut.GetItemAsStringAsync(Key); + // Act + var result = await _sut.GetItemAsStringAsync(Key); - // Assert - var serializedData = _serializer.Serialize(objectToSave); - Assert.Equal(serializedData, result); - } + // Assert + var serializedData = _serializer.Serialize(objectToSave); + Assert.Equal(serializedData, result); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs index cba032e..67de73f 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/GetItemAsync.cs @@ -10,97 +10,96 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class GetItemAsync { - public class GetItemAsync - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; - private const string Key = "testKey"; + private const string Key = "testKey"; - public GetItemAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } + public GetItemAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - var action = new Func(async () => await _sut.GetItemAsync(key)); + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Func(async () => await _sut.GetItemAsync(key)); - // assert - Assert.ThrowsAsync(action); - } + // assert + Assert.ThrowsAsync(action); + } - [Theory] - [InlineData("Item1", "stringTest")] - [InlineData("Item2", 11)] - [InlineData("Item3", 11.11)] - public async Task ReturnsDeserializedDataFromStore(string key, T data) - { - // Arrange - await _sut.SetItemAsync(key, data); + [Theory] + [InlineData("Item1", "stringTest")] + [InlineData("Item2", 11)] + [InlineData("Item3", 11.11)] + public async Task ReturnsDeserializedDataFromStore(string key, T data) + { + // Arrange + await _sut.SetItemAsync(key, data); - // Act - var result = await _sut.GetItemAsync(key); + // Act + var result = await _sut.GetItemAsync(key); - // Assert - Assert.Equal(data, result); - } + // Assert + Assert.Equal(data, result); + } - [Fact] - public async Task ReturnsComplexObjectFromStore() - { - // Arrange - var objectToSave = new TestObject(2, "Jane Smith"); - await _sut.SetItemAsync(Key, objectToSave); + [Fact] + public async Task ReturnsComplexObjectFromStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + await _sut.SetItemAsync(Key, objectToSave); - // Act - var result = await _sut.GetItemAsync(Key); + // Act + var result = await _sut.GetItemAsync(Key); - // Assert - Assert.Equal(objectToSave.Id, result.Id); - Assert.Equal(objectToSave.Name, result.Name); - } + // Assert + Assert.Equal(objectToSave.Id, result.Id); + Assert.Equal(objectToSave.Name, result.Name); + } - [Fact] - public async Task ReturnsNullFromStore_When_NullValueSaved() - { - // Arrange - var valueToSave = (string)null; - await _sut.SetItemAsync(Key, valueToSave); + [Fact] + public async Task ReturnsNullFromStore_When_NullValueSaved() + { + // Arrange + var valueToSave = (string)null; + await _sut.SetItemAsync(Key, valueToSave); - // Act - var result = await _sut.GetItemAsync(Key); + // Act + var result = await _sut.GetItemAsync(Key); - // Assert - Assert.Null(result); - } + // Assert + Assert.Null(result); + } - [Fact] - public async Task ReturnsStringFromStore_When_JsonExceptionIsThrown() - { - // Arrange - var valueToSave = "[{ id: 5, name: \"Jane Smith\"}]"; - await _storageProvider.SetItemAsync(Key, valueToSave); + [Fact] + public async Task ReturnsStringFromStore_When_JsonExceptionIsThrown() + { + // Arrange + var valueToSave = "[{ id: 5, name: \"Jane Smith\"}]"; + await _storageProvider.SetItemAsync(Key, valueToSave); - // Act - var result = await _sut.GetItemAsync(Key); + // Act + var result = await _sut.GetItemAsync(Key); - // Assert - Assert.Equal(valueToSave, result); - } + // Assert + Assert.Equal(valueToSave, result); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs index 5082df1..ac8cba3 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Key.cs @@ -8,55 +8,54 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class Key { - public class Key - { - private readonly SessionStorageService _sut; + private readonly SessionStorageService _sut; - public Key() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - IStorageProvider storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(storageProvider, serializer); - } + public Key() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } - [Fact] - public void ReturnsNameOfKeyAtIndex_When_KeyExistsInStore() - { - // Arrange - const string key1 = "TestKey1"; - const string key2 = "TestKey2"; + [Fact] + public void ReturnsNameOfKeyAtIndex_When_KeyExistsInStore() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; - var item1 = new TestObject(1, "Jane Smith"); - var item2 = new TestObject(2, "John Smith"); + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); - _sut.SetItem(key1, item1); - _sut.SetItem(key2, item2); + _sut.SetItem(key1, item1); + _sut.SetItem(key2, item2); - // Act - var keyName = _sut.Key(1); + // Act + var keyName = _sut.Key(1); - // Assert - Assert.Equal(key2, keyName); - } + // Assert + Assert.Equal(key2, keyName); + } - [Fact] - public void ReturnsNull_When_KeyDoesNotExistInStore() - { - // Arrange - var item1 = new TestObject(1, "Jane Smith"); - _sut.SetItem("TestKey1", item1); + [Fact] + public void ReturnsNull_When_KeyDoesNotExistInStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + _sut.SetItem("TestKey1", item1); - // Act - var keyName = _sut.Key(1); + // Act + var keyName = _sut.Key(1); - // Assert - Assert.Null(keyName); - } + // Assert + Assert.Null(keyName); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs index 86dec29..0ec8a92 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/KeyAsync.cs @@ -9,55 +9,54 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class KeyAsync { - public class KeyAsync - { - private readonly SessionStorageService _sut; + private readonly SessionStorageService _sut; - public KeyAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - IStorageProvider storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(storageProvider, serializer); - } + public KeyAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } - [Fact] - public async Task ReturnsNameOfKeyAtIndex_When_KeyExistsInStore() - { - // Arrange - const string key1 = "TestKey1"; - const string key2 = "TestKey2"; + [Fact] + public async Task ReturnsNameOfKeyAtIndex_When_KeyExistsInStore() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; - var item1 = new TestObject(1, "Jane Smith"); - var item2 = new TestObject(2, "John Smith"); + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); - await _sut.SetItemAsync(key1, item1); - await _sut.SetItemAsync(key2, item2); + await _sut.SetItemAsync(key1, item1); + await _sut.SetItemAsync(key2, item2); - // Act - var keyName = await _sut.KeyAsync(1); + // Act + var keyName = await _sut.KeyAsync(1); - // Assert - Assert.Equal(key2, keyName); - } + // Assert + Assert.Equal(key2, keyName); + } - [Fact] - public async Task ReturnsNull_When_KeyDoesNotExistInStore() - { - // Arrange - var item1 = new TestObject(1, "Jane Smith"); - await _sut.SetItemAsync("TestKey1", item1); + [Fact] + public async Task ReturnsNull_When_KeyDoesNotExistInStore() + { + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + await _sut.SetItemAsync("TestKey1", item1); - // Act - var keyName = await _sut.KeyAsync(1); + // Act + var keyName = await _sut.KeyAsync(1); - // Assert - Assert.Null(keyName); - } + // Assert + Assert.Null(keyName); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Keys.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Keys.cs new file mode 100644 index 0000000..d243c19 --- /dev/null +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Keys.cs @@ -0,0 +1,153 @@ +using System.Linq; +using System.Text.Json; +using Blazored.SessionStorage.JsonConverters; +using Blazored.SessionStorage.Serialization; +using Blazored.SessionStorage.StorageOptions; +using Blazored.SessionStorage.TestExtensions; +using Blazored.SessionStorage.Tests.TestAssets; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class Keys + { + private readonly SessionStorageService _sut; + + public Keys() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public void ReturnsKeysAsync() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; + _sut.Clear(); + + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + _sut.SetItem(key1, item1); + _sut.SetItem(key2, item2); + + // Act + var keyNames = _sut.KeysAsync().Result.ToList(); + + // Assert + Assert.Collection(keyNames, + item => Assert.Equal(key1, item), + item => Assert.Equal(key2, item)); + } + + [Fact] + public void ReturnsKeys() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; + _sut.Clear(); + + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + + _sut.SetItem(key1, item1); + _sut.SetItem(key2, item2); + + // Act + var keyNames = _sut.Keys().ToList(); + + // Assert + Assert.Collection(keyNames, + item => Assert.Equal(key1, item), + item => Assert.Equal(key2, item)); + } + + [Fact] + public void ReturnsEmptyWhenCollectionEmptyAsync() + { + // Arrange + _sut.Clear(); + + // Act + var keyNames = _sut.KeysAsync().Result; + + // Assert + Assert.Empty(keyNames); + } + + [Fact] + public void ReturnsEmptyWhenCollectionEmpty() + { + // Arrange + _sut.Clear(); + + // Act + var keyNames = _sut.Keys(); + + // Assert + Assert.Empty(keyNames); + } + + [Fact] + public void RemoveKeysAsync() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; + const string key3 = "TestKey3"; + _sut.Clear(); + + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + var item3 = new TestObject(3, "Jade Smith"); + + _sut.SetItem(key1, item1); + _sut.SetItem(key2, item2); + _sut.SetItem(key3, item3); + + // Act + var keyNames = new[] { key1, key2 }; + _sut.RemoveItemsAsync(keyNames).AsTask().Wait(); + + // Assert + Assert.Equal(1, _sut.Length()); + Assert.NotNull(_sut.GetItem(key3)); + } + + [Fact] + public void RemoveKeys() + { + // Arrange + const string key1 = "TestKey1"; + const string key2 = "TestKey2"; + const string key3 = "TestKey3"; + _sut.Clear(); + + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); + var item3 = new TestObject(3, "Jade Smith"); + + _sut.SetItem(key1, item1); + _sut.SetItem(key2, item2); + _sut.SetItem(key3, item3); + + // Act + var keyNames = new[] { key1, key2 }; + _sut.RemoveItems(keyNames); + + // Assert + Assert.Equal(1, _sut.Length()); + Assert.NotNull(_sut.GetItem(key3)); + } + } diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs index 94d189d..aaecf4e 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/Length.cs @@ -8,48 +8,47 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class Length { - public class Length + private readonly SessionStorageService _sut; + + public Length() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public void ReturnsZeroWhenStoreIsEmpty() + { + // Act + var itemCount = _sut.Length(); + + // Assert + Assert.Equal(0, itemCount); + } + + [Fact] + public void ReturnsNumberOfItemsInStore() { - private readonly SessionStorageService _sut; - - public Length() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - IStorageProvider storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(storageProvider, serializer); - } - - [Fact] - public void ReturnsZeroWhenStoreIsEmpty() - { - // Act - var itemCount = _sut.Length(); - - // Assert - Assert.Equal(0, itemCount); - } - - [Fact] - public void ReturnsNumberOfItemsInStore() - { - // Arrange - var item1 = new TestObject(1, "Jane Smith"); - var item2 = new TestObject(2, "John Smith"); + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); - _sut.SetItem("Item1", item1); - _sut.SetItem("Item2", item2); + _sut.SetItem("Item1", item1); + _sut.SetItem("Item2", item2); - // Act - var itemCount = _sut.Length(); + // Act + var itemCount = _sut.Length(); - // Assert - Assert.Equal(2, itemCount); - } + // Assert + Assert.Equal(2, itemCount); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs index 6cc547e..700780d 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/LengthAsync.cs @@ -9,48 +9,47 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class LengthAsync { - public class LengthAsync + private readonly SessionStorageService _sut; + + public LengthAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + IStorageProvider storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(storageProvider, serializer); + } + + [Fact] + public async Task ReturnsZeroWhenStoreIsEmpty() + { + // Act + var itemCount = await _sut.LengthAsync(); + + // Assert + Assert.Equal(0, itemCount); + } + + [Fact] + public async Task ReturnsNumberOfItemsInStore() { - private readonly SessionStorageService _sut; - - public LengthAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - IStorageProvider storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(storageProvider, serializer); - } - - [Fact] - public async Task ReturnsZeroWhenStoreIsEmpty() - { - // Act - var itemCount = await _sut.LengthAsync(); - - // Assert - Assert.Equal(0, itemCount); - } - - [Fact] - public async Task ReturnsNumberOfItemsInStore() - { - // Arrange - var item1 = new TestObject(1, "Jane Smith"); - var item2 = new TestObject(2, "John Smith"); + // Arrange + var item1 = new TestObject(1, "Jane Smith"); + var item2 = new TestObject(2, "John Smith"); - await _sut.SetItemAsync("Item1", item1); - await _sut.SetItemAsync("Item2", item2); + await _sut.SetItemAsync("Item1", item1); + await _sut.SetItemAsync("Item2", item2); - // Act - var itemCount = await _sut.LengthAsync(); + // Act + var itemCount = await _sut.LengthAsync(); - // Assert - Assert.Equal(2, itemCount); - } + // Assert + Assert.Equal(2, itemCount); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs index bc088d4..1433b44 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItem.cs @@ -9,61 +9,60 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class RemoveItem { - public class RemoveItem - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; - private const string Key = "testKey"; + private const string Key = "testKey"; - public RemoveItem() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, serializer); - } + public RemoveItem() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - var action = new Action(() => _sut.RemoveItem(key)); + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Action(() => _sut.RemoveItem(key)); - // assert - Assert.Throws(action); - } + // assert + Assert.Throws(action); + } - [Fact] - public void RemovesItemFromStoreIfExists() - { - // Arrange - var data = new TestObject(2, "Jane Smith"); - _sut.SetItem(Key, data); + [Fact] + public void RemovesItemFromStoreIfExists() + { + // Arrange + var data = new TestObject(2, "Jane Smith"); + _sut.SetItem(Key, data); - // Act - _sut.RemoveItem(Key); + // Act + _sut.RemoveItem(Key); - // Assert - Assert.Equal(0, _storageProvider.Length()); - } + // Assert + Assert.Equal(0, _storageProvider.Length()); + } - [Fact] - public void DoesNothingWhenItemDoesNotExistInStore() - { - // Act - _sut.RemoveItem(Key); + [Fact] + public void DoesNothingWhenItemDoesNotExistInStore() + { + // Act + _sut.RemoveItem(Key); - // Assert - Assert.Equal(0, _storageProvider.Length()); - } + // Assert + Assert.Equal(0, _storageProvider.Length()); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs index 56f27c4..cd138ee 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/RemoveItemAsync.cs @@ -10,61 +10,60 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class RemoveItemAsync { - public class RemoveItemAsync - { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; - private const string Key = "testKey"; + private const string Key = "testKey"; - public RemoveItemAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, serializer); - } + public RemoveItemAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + IJsonSerializer serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, serializer); + } - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - var action = new Func(async () => await _sut.RemoveItemAsync(key)); + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + var action = new Func(async () => await _sut.RemoveItemAsync(key)); - // assert - Assert.ThrowsAsync(action); - } + // assert + Assert.ThrowsAsync(action); + } - [Fact] - public async Task RemovesItemFromStoreIfExists() - { - // Arrange - var data = new TestObject(2, "Jane Smith"); - await _sut.SetItemAsync(Key, data); + [Fact] + public async Task RemovesItemFromStoreIfExists() + { + // Arrange + var data = new TestObject(2, "Jane Smith"); + await _sut.SetItemAsync(Key, data); - // Act - await _sut.RemoveItemAsync(Key); + // Act + await _sut.RemoveItemAsync(Key); - // Assert - Assert.Equal(0, await _storageProvider.LengthAsync()); - } + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } - [Fact] - public async Task DoesNothingWhenItemDoesNotExistInStore() - { - // Act - await _sut.RemoveItemAsync(Key); + [Fact] + public async Task DoesNothingWhenItemDoesNotExistInStore() + { + // Act + await _sut.RemoveItemAsync(Key); - // Assert - Assert.Equal(0, await _storageProvider.LengthAsync()); - } + // Assert + Assert.Equal(0, await _storageProvider.LengthAsync()); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs index f722851..48c3f92 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItem.cs @@ -9,226 +9,225 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class SetItem { - public class SetItem + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItem() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; - - private const string Key = "testKey"; - - public SetItem() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - const string data = "Data"; - var action = new Action(() => _sut.SetItem(key, data)); - - // assert - Assert.Throws(action); - } - - [Fact] - public void RaisesOnChangingEvent_When_SavingNewData() - { - // arrange - var onChangingCalled = false; - _sut.Changing += (_, _) => onChangingCalled = true; - - // act - _sut.SetItem("Key", "Data"); - - // assert - Assert.True(onChangingCalled); - } + // arrange / act + const string data = "Data"; + var action = new Action(() => _sut.SetItem(key, data)); + + // assert + Assert.Throws(action); + } + + [Fact] + public void RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (_, _) => onChangingCalled = true; + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } - [Fact] - public void OnChangingEventContainsEmptyOldValue_When_SavingData() - { - // arrange - var oldValue = ""; - _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - _sut.SetItem("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public void OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public void OnChangingEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); - - // act - _sut.SetItemAsync("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public void OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItemAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public void OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() - { - // arrange - _sut.Changing += (_, args) => args.Cancel = true; - - // act - _sut.SetItem("Key", "Data"); - - // assert - Assert.Equal(0, _storageProvider.Length()); - } + [Fact] + public void OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(0, _storageProvider.Length()); + } - [Theory] - [InlineData("stringTest")] - [InlineData(11)] - [InlineData(11.11)] - public void SavesDataAsJsonToStore(T valueToSave) - { - // Act - _sut.SetItem(Key, valueToSave); - - // Assert - var serializedValue = _storageProvider.GetItem(Key); - var valueFromStore = _serializer.Deserialize(serializedValue); - - Assert.Equal(1, _storageProvider.Length()); - Assert.Equal(valueToSave, valueFromStore); - } - - [Fact] - public void SavesComplexObjectAsJsonToStore() - { - // Arrange - var objectToSave = new TestObject(2, "Jane Smith"); - - // Act - _sut.SetItem(Key, objectToSave); - - // Assert - var serializedObject = _storageProvider.GetItem(Key); - var objectFromStore = _serializer.Deserialize(serializedObject); - - Assert.Equal(1, _storageProvider.Length()); - Assert.Equal(objectToSave.Id, objectFromStore.Id); - Assert.Equal(objectToSave.Name, objectFromStore.Name); - } + [Theory] + [InlineData("stringTest")] + [InlineData(11)] + [InlineData(11.11)] + public void SavesDataAsJsonToStore(T valueToSave) + { + // Act + _sut.SetItem(Key, valueToSave); + + // Assert + var serializedValue = _storageProvider.GetItem(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(valueToSave, valueFromStore); + } + + [Fact] + public void SavesComplexObjectAsJsonToStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + + // Act + _sut.SetItem(Key, objectToSave); + + // Assert + var serializedObject = _storageProvider.GetItem(Key); + var objectFromStore = _serializer.Deserialize(serializedObject); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(objectToSave.Id, objectFromStore.Id); + Assert.Equal(objectToSave.Name, objectFromStore.Name); + } - [Fact] - public void SavesNullIntoStore_When_NullValueProvided() - { - // Arrange - var valueToSave = (string)null; - - // Act - _sut.SetItem(Key, valueToSave); - - // Assert - var serializedValue = _storageProvider.GetItem(Key); - var valueFromStore = _serializer.Deserialize(serializedValue); - - Assert.Equal(1, _storageProvider.Length()); - Assert.Null(valueFromStore); - } + [Fact] + public void SavesNullIntoStore_When_NullValueProvided() + { + // Arrange + var valueToSave = (string)null; + + // Act + _sut.SetItem(Key, valueToSave); + + // Assert + var serializedValue = _storageProvider.GetItem(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Null(valueFromStore); + } - [Fact] - public void OverwriteExistingValueInStore_When_UsingTheSameKey() - { - // Arrange - const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; - const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + [Fact] + public void OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; - var serializedValue = _serializer.Serialize(existingValue); - _storageProvider.SetItem(Key, serializedValue); + var serializedValue = _serializer.Serialize(existingValue); + _storageProvider.SetItem(Key, serializedValue); - // Act - _sut.SetItem(Key, newValue); + // Act + _sut.SetItem(Key, newValue); - // Assert - var serializedUpdatedValue = _storageProvider.GetItem(Key); - var updatedValue = _serializer.Deserialize(serializedUpdatedValue); + // Assert + var serializedUpdatedValue = _storageProvider.GetItem(Key); + var updatedValue = _serializer.Deserialize(serializedUpdatedValue); - Assert.Equal(newValue, updatedValue); - } + Assert.Equal(newValue, updatedValue); + } - [Fact] - public void RaisesOnChangedEvent_When_SavingData() - { - // arrange - var onChangedCalled = false; - _sut.Changed += (_, _) => onChangedCalled = true; - - // act - _sut.SetItem("Key", "Data"); - - // assert - Assert.True(onChangedCalled); - } + [Fact] + public void RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (_, _) => onChangedCalled = true; + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } - [Fact] - public void OnChangedEventContainsEmptyOldValue_When_SavingNewData() - { - // arrange - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - _sut.SetItem("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public void OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public void OnChangedEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); - - // act - _sut.SetItem("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public void OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItem("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public void OnChangedEventContainsOldValue_When_UpdatingExistingData() - { - // arrange - var existingValue = "Foo"; - _storageProvider.SetItem("Key", existingValue); - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - _sut.SetItem("Key", "Data"); - - // assert - Assert.Equal(existingValue, oldValue); - } + [Fact] + public void OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + _storageProvider.SetItem("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItem("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs index 24ec2fd..913b043 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsString.cs @@ -8,197 +8,196 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class SetItemAsString { - public class SetItemAsString + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItemAsString() { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; - - private const string Key = "testKey"; - - public SetItemAsString() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - const string data = "Data"; - var action = new Action(() => _sut.SetItemAsString(key, data)); - - // assert - Assert.Throws(action); - } + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + const string data = "Data"; + var action = new Action(() => _sut.SetItemAsString(key, data)); + + // assert + Assert.Throws(action); + } - [Fact] - public void ThrowsArgumentNullException_When_DataIsNull() - { - // arrange / act - var data = (string)null; - var action = new Action(() => _sut.SetItemAsString("MyValue", data)); - - // assert - Assert.Throws(action); - } - - [Fact] - public void RaisesOnChangingEvent_When_SavingNewData() - { - // arrange - var onChangingCalled = false; - _sut.Changing += (_, _) => onChangingCalled = true; - - // act - _sut.SetItemAsString("Key", "Data"); - - // assert - Assert.True(onChangingCalled); - } + [Fact] + public void ThrowsArgumentNullException_When_DataIsNull() + { + // arrange / act + var data = (string)null; + var action = new Action(() => _sut.SetItemAsString("MyValue", data)); + + // assert + Assert.Throws(action); + } + + [Fact] + public void RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (_, _) => onChangingCalled = true; + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } - [Fact] - public void OnChangingEventContainsEmptyOldValue_When_SavingData() - { - // arrange - var oldValue = ""; - _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - _sut.SetItemAsString("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public void OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public void OnChangingEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); - - // act - _sut.SetItemAsString("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public void OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItemAsString("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public void OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() - { - // arrange - _sut.Changing += (_, args) => args.Cancel = true; - - // act - _sut.SetItemAsString("Key", "Data"); - - // assert - Assert.Equal(0, _storageProvider.Length()); - } + [Fact] + public void OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(0, _storageProvider.Length()); + } - [Fact] - public void SavesDataToStore() - { - // Act - var valueToSave = "StringValue"; - _sut.SetItemAsString(Key, valueToSave); - - // Assert - var valueFromStore = _storageProvider.GetItem(Key); - - Assert.Equal(1, _storageProvider.Length()); - Assert.Equal(valueToSave, valueFromStore); - } + [Fact] + public void SavesDataToStore() + { + // Act + var valueToSave = "StringValue"; + _sut.SetItemAsString(Key, valueToSave); + + // Assert + var valueFromStore = _storageProvider.GetItem(Key); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(valueToSave, valueFromStore); + } - [Fact] - public void OverwriteExistingValueInStore_When_UsingTheSameKey() - { - // Arrange - const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; - const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + [Fact] + public void OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; - _storageProvider.SetItem(Key, existingValue); + _storageProvider.SetItem(Key, existingValue); - // Act - _sut.SetItemAsString(Key, newValue); + // Act + _sut.SetItemAsString(Key, newValue); - // Assert - var updatedValue = _storageProvider.GetItem(Key); + // Assert + var updatedValue = _storageProvider.GetItem(Key); - Assert.Equal(newValue, updatedValue); - } + Assert.Equal(newValue, updatedValue); + } - [Fact] - public void RaisesOnChangedEvent_When_SavingData() - { - // arrange - var onChangedCalled = false; - _sut.Changed += (_, _) => onChangedCalled = true; - - // act - _sut.SetItemAsString("Key", "Data"); - - // assert - Assert.True(onChangedCalled); - } + [Fact] + public void RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (_, _) => onChangedCalled = true; + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } - [Fact] - public void OnChangedEventContainsEmptyOldValue_When_SavingNewData() - { - // arrange - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - _sut.SetItemAsString("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public void OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public void OnChangedEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); - - // act - _sut.SetItemAsString("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public void OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + _sut.SetItemAsString("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public void OnChangedEventContainsOldValue_When_UpdatingExistingData() - { - // arrange - var existingValue = "Foo"; - _storageProvider.SetItem("Key", existingValue); - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - _sut.SetItemAsString("Key", "Data"); - - // assert - Assert.Equal(existingValue, oldValue); - } + [Fact] + public void OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + _storageProvider.SetItem("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + _sut.SetItemAsString("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs index 4a5d0a2..7857a1b 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsStringAsync.cs @@ -9,197 +9,196 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class SetItemAsStringAsync { - public class SetItemAsStringAsync + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItemAsStringAsync() { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; - - private const string Key = "testKey"; - - public SetItemAsStringAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - const string data = "Data"; - var action = new Func(async () => await _sut.SetItemAsStringAsync(key, data)); - - // assert - Assert.ThrowsAsync(action); - } + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) + { + // arrange / act + const string data = "Data"; + var action = new Func(async () => await _sut.SetItemAsStringAsync(key, data)); + + // assert + Assert.ThrowsAsync(action); + } - [Fact] - public void ThrowsArgumentNullException_When_DataIsNull() - { - // arrange / act - var data = (string)null; - var action = new Func(async () => await _sut.SetItemAsStringAsync("MyValue", data)); - - // assert - Assert.ThrowsAsync(action); - } - - [Fact] - public async Task RaisesOnChangingEvent_When_SavingNewData() - { - // arrange - var onChangingCalled = false; - _sut.Changing += (_, _) => onChangingCalled = true; - - // act - await _sut.SetItemAsStringAsync("Key", "Data"); - - // assert - Assert.True(onChangingCalled); - } + [Fact] + public void ThrowsArgumentNullException_When_DataIsNull() + { + // arrange / act + var data = (string)null; + var action = new Func(async () => await _sut.SetItemAsStringAsync("MyValue", data)); + + // assert + Assert.ThrowsAsync(action); + } + + [Fact] + public async Task RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (_, _) => onChangingCalled = true; + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } - [Fact] - public async Task OnChangingEventContainsEmptyOldValue_When_SavingData() - { - // arrange - var oldValue = ""; - _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - await _sut.SetItemAsStringAsync("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public async Task OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public async Task OnChangingEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); - - // act - await _sut.SetItemAsStringAsync("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public async Task OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public async Task OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() - { - // arrange - _sut.Changing += (_, args) => args.Cancel = true; - - // act - await _sut.SetItemAsStringAsync("Key", "Data"); - - // assert - Assert.Equal(0, _storageProvider.Length()); - } + [Fact] + public async Task OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(0, _storageProvider.Length()); + } - [Fact] - public async Task SavesDataToStore() - { - // Act - var valueToSave = "StringValue"; - await _sut.SetItemAsStringAsync(Key, valueToSave); - - // Assert - var valueFromStore = _storageProvider.GetItem(Key); - - Assert.Equal(1, _storageProvider.Length()); - Assert.Equal(valueToSave, valueFromStore); - } + [Fact] + public async Task SavesDataToStore() + { + // Act + var valueToSave = "StringValue"; + await _sut.SetItemAsStringAsync(Key, valueToSave); + + // Assert + var valueFromStore = _storageProvider.GetItem(Key); + + Assert.Equal(1, _storageProvider.Length()); + Assert.Equal(valueToSave, valueFromStore); + } - [Fact] - public async Task OverwriteExistingValueInStore_When_UsingTheSameKey() - { - // Arrange - const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; - const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + [Fact] + public async Task OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; - _storageProvider.SetItem(Key, existingValue); + _storageProvider.SetItem(Key, existingValue); - // Act - await _sut.SetItemAsStringAsync(Key, newValue); + // Act + await _sut.SetItemAsStringAsync(Key, newValue); - // Assert - var updatedValue = _storageProvider.GetItem(Key); + // Assert + var updatedValue = _storageProvider.GetItem(Key); - Assert.Equal(newValue, updatedValue); - } + Assert.Equal(newValue, updatedValue); + } - [Fact] - public async Task RaisesOnChangedEvent_When_SavingData() - { - // arrange - var onChangedCalled = false; - _sut.Changed += (_, _) => onChangedCalled = true; - - // act - await _sut.SetItemAsStringAsync("Key", "Data"); - - // assert - Assert.True(onChangedCalled); - } + [Fact] + public async Task RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (_, _) => onChangedCalled = true; + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } - [Fact] - public async Task OnChangedEventContainsEmptyOldValue_When_SavingNewData() - { - // arrange - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - await _sut.SetItemAsStringAsync("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public async Task OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public async Task OnChangedEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); - - // act - await _sut.SetItemAsStringAsync("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public async Task OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public async Task OnChangedEventContainsOldValue_When_UpdatingExistingData() - { - // arrange - var existingValue = "Foo"; - _storageProvider.SetItem("Key", existingValue); - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - await _sut.SetItemAsStringAsync("Key", "Data"); - - // assert - Assert.Equal(existingValue, oldValue); - } + [Fact] + public async Task OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + _storageProvider.SetItem("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsStringAsync("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs index df49036..8aa1e49 100644 --- a/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs +++ b/tests/Blazored.SessionStorage.Tests/SessionStorageServiceTests/SetItemAsync.cs @@ -10,226 +10,225 @@ using Moq; using Xunit; -namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests +namespace Blazored.SessionStorage.Tests.SessionStorageServiceTests; + +public class SetItemAsync { - public class SetItemAsync + private readonly SessionStorageService _sut; + private readonly IStorageProvider _storageProvider; + private readonly IJsonSerializer _serializer; + + private const string Key = "testKey"; + + public SetItemAsync() + { + var mockOptions = new Mock>(); + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new TimespanJsonConverter()); + mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); + _serializer = new SystemTextJsonSerializer(mockOptions.Object); + _storageProvider = new InMemoryStorageProvider(); + _sut = new SessionStorageService(_storageProvider, _serializer); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) { - private readonly SessionStorageService _sut; - private readonly IStorageProvider _storageProvider; - private readonly IJsonSerializer _serializer; - - private const string Key = "testKey"; - - public SetItemAsync() - { - var mockOptions = new Mock>(); - var jsonOptions = new JsonSerializerOptions(); - jsonOptions.Converters.Add(new TimespanJsonConverter()); - mockOptions.Setup(u => u.Value).Returns(new SessionStorageOptions()); - _serializer = new SystemTextJsonSerializer(mockOptions.Object); - _storageProvider = new InMemoryStorageProvider(); - _sut = new SessionStorageService(_storageProvider, _serializer); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(null)] - public void ThrowsArgumentNullException_When_KeyIsInvalid(string key) - { - // arrange / act - const string data = "Data"; - var action = new Func(async () => await _sut.SetItemAsync(key, data)); - - // assert - Assert.ThrowsAsync(action); - } - - [Fact] - public async Task RaisesOnChangingEvent_When_SavingNewData() - { - // arrange - var onChangingCalled = false; - _sut.Changing += (sender, args) => onChangingCalled = true; - - // act - await _sut.SetItemAsync("Key", "Data"); - - // assert - Assert.True(onChangingCalled); - } + // arrange / act + const string data = "Data"; + var action = new Func(async () => await _sut.SetItemAsync(key, data)); + + // assert + Assert.ThrowsAsync(action); + } + + [Fact] + public async Task RaisesOnChangingEvent_When_SavingNewData() + { + // arrange + var onChangingCalled = false; + _sut.Changing += (sender, args) => onChangingCalled = true; + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.True(onChangingCalled); + } - [Fact] - public async Task OnChangingEventContainsEmptyOldValue_When_SavingData() - { - // arrange - var oldValue = ""; - _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - await _sut.SetItemAsync("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public async Task OnChangingEventContainsEmptyOldValue_When_SavingData() + { + // arrange + var oldValue = ""; + _sut.Changing += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public async Task OnChangingEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); - - // act - await _sut.SetItemAsync("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public async Task OnChangingEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changing += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public async Task OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() - { - // arrange - _sut.Changing += (_, args) => args.Cancel = true; - - // act - await _sut.SetItemAsync("Key", "Data"); - - // assert - Assert.Equal(0, await _storageProvider.LengthAsync()); - } + [Fact] + public async Task OnChangingEventIsCancelled_When_SettingCancelToTrue_When_SavingNewData() + { + // arrange + _sut.Changing += (_, args) => args.Cancel = true; + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(0, await _storageProvider.LengthAsync()); + } - [Theory] - [InlineData("stringTest")] - [InlineData(11)] - [InlineData(11.11)] - public async Task SavesDataAsJsonToStore(T valueToSave) - { - // Act - await _sut.SetItemAsync(Key, valueToSave); - - // Assert - var serializedValue = await _storageProvider.GetItemAsync(Key); - var valueFromStore = _serializer.Deserialize(serializedValue); - - Assert.Equal(1, await _storageProvider.LengthAsync()); - Assert.Equal(valueToSave, valueFromStore); - } - - [Fact] - public async Task SavesComplexObjectAsJsonToStore() - { - // Arrange - var objectToSave = new TestObject(2, "Jane Smith"); - - // Act - await _sut.SetItemAsync(Key, objectToSave); - - // Assert - var serializedObject = await _storageProvider.GetItemAsync(Key); - var objectFromStore = _serializer.Deserialize(serializedObject); - - Assert.Equal(1, await _storageProvider.LengthAsync()); - Assert.Equal(objectToSave.Id, objectFromStore.Id); - Assert.Equal(objectToSave.Name, objectFromStore.Name); - } + [Theory] + [InlineData("stringTest")] + [InlineData(11)] + [InlineData(11.11)] + public async Task SavesDataAsJsonToStore(T valueToSave) + { + // Act + await _sut.SetItemAsync(Key, valueToSave); + + // Assert + var serializedValue = await _storageProvider.GetItemAsync(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, await _storageProvider.LengthAsync()); + Assert.Equal(valueToSave, valueFromStore); + } + + [Fact] + public async Task SavesComplexObjectAsJsonToStore() + { + // Arrange + var objectToSave = new TestObject(2, "Jane Smith"); + + // Act + await _sut.SetItemAsync(Key, objectToSave); + + // Assert + var serializedObject = await _storageProvider.GetItemAsync(Key); + var objectFromStore = _serializer.Deserialize(serializedObject); + + Assert.Equal(1, await _storageProvider.LengthAsync()); + Assert.Equal(objectToSave.Id, objectFromStore.Id); + Assert.Equal(objectToSave.Name, objectFromStore.Name); + } - [Fact] - public async Task SavesNullIntoStore_When_NullValueProvided() - { - // Arrange - var valueToSave = (string)null; - - // Act - await _sut.SetItemAsync(Key, valueToSave); - - // Assert - var serializedValue = await _storageProvider.GetItemAsync(Key); - var valueFromStore = _serializer.Deserialize(serializedValue); - - Assert.Equal(1, await _storageProvider.LengthAsync()); - Assert.Null(valueFromStore); - } + [Fact] + public async Task SavesNullIntoStore_When_NullValueProvided() + { + // Arrange + var valueToSave = (string)null; + + // Act + await _sut.SetItemAsync(Key, valueToSave); + + // Assert + var serializedValue = await _storageProvider.GetItemAsync(Key); + var valueFromStore = _serializer.Deserialize(serializedValue); + + Assert.Equal(1, await _storageProvider.LengthAsync()); + Assert.Null(valueFromStore); + } - [Fact] - public async Task OverwriteExistingValueInStore_When_UsingTheSameKey() - { - // Arrange - const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; - const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; + [Fact] + public async Task OverwriteExistingValueInStore_When_UsingTheSameKey() + { + // Arrange + const string existingValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZXhwIjoxNTg1NjYwNzEyLCJpc3MiOiJDb2RlUmVkQm9va2luZy5TZXJ2ZXIiLCJhdWQiOiJDb2RlUmVkQm9va2luZy5DbGllbnRzIn0.JhK1M1H7NLCFexujJYCDjTn9La0HloGYADMHXGCFksU"; + const string newValue = "6QLE0LL7iw7tHPAwold31qUENt3lVTUZxDGqeXQFx38="; - var serializedValue = _serializer.Serialize(existingValue); - await _storageProvider.SetItemAsync(Key, serializedValue); + var serializedValue = _serializer.Serialize(existingValue); + await _storageProvider.SetItemAsync(Key, serializedValue); - // Act - await _sut.SetItemAsync(Key, newValue); + // Act + await _sut.SetItemAsync(Key, newValue); - // Assert - var serializedUpdatedValue = await _storageProvider.GetItemAsync(Key); - var updatedValue = _serializer.Deserialize(serializedUpdatedValue); + // Assert + var serializedUpdatedValue = await _storageProvider.GetItemAsync(Key); + var updatedValue = _serializer.Deserialize(serializedUpdatedValue); - Assert.Equal(newValue, updatedValue); - } + Assert.Equal(newValue, updatedValue); + } - [Fact] - public async Task RaisesOnChangedEvent_When_SavingData() - { - // arrange - var onChangedCalled = false; - _sut.Changed += (sender, args) => onChangedCalled = true; - - // act - await _sut.SetItemAsync("Key", "Data"); - - // assert - Assert.True(onChangedCalled); - } + [Fact] + public async Task RaisesOnChangedEvent_When_SavingData() + { + // arrange + var onChangedCalled = false; + _sut.Changed += (sender, args) => onChangedCalled = true; + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.True(onChangedCalled); + } - [Fact] - public async Task OnChangedEventContainsEmptyOldValue_When_SavingNewData() - { - // arrange - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - await _sut.SetItemAsync("Key", "Data"); - - // assert - Assert.Equal(default, oldValue); - } + [Fact] + public async Task OnChangedEventContainsEmptyOldValue_When_SavingNewData() + { + // arrange + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(default, oldValue); + } - [Fact] - public async Task OnChangedEventContainsNewValue_When_SavingNewData() - { - // arrange - const string data = "Data"; - var newValue = ""; - _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); - - // act - await _sut.SetItemAsync("Key", data); - - // assert - Assert.Equal(data, newValue); - } + [Fact] + public async Task OnChangedEventContainsNewValue_When_SavingNewData() + { + // arrange + const string data = "Data"; + var newValue = ""; + _sut.Changed += (_, args) => newValue = args.NewValue.ToString(); + + // act + await _sut.SetItemAsync("Key", data); + + // assert + Assert.Equal(data, newValue); + } - [Fact] - public async Task OnChangedEventContainsOldValue_When_UpdatingExistingData() - { - // arrange - var existingValue = "Foo"; - await _storageProvider.SetItemAsync("Key", existingValue); - var oldValue = ""; - _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); - - // act - await _sut.SetItemAsync("Key", "Data"); - - // assert - Assert.Equal(existingValue, oldValue); - } + [Fact] + public async Task OnChangedEventContainsOldValue_When_UpdatingExistingData() + { + // arrange + var existingValue = "Foo"; + await _storageProvider.SetItemAsync("Key", existingValue); + var oldValue = ""; + _sut.Changed += (_, args) => oldValue = args.OldValue?.ToString(); + + // act + await _sut.SetItemAsync("Key", "Data"); + + // assert + Assert.Equal(existingValue, oldValue); } -} +} \ No newline at end of file diff --git a/tests/Blazored.SessionStorage.Tests/TestAssets/TestObject.cs b/tests/Blazored.SessionStorage.Tests/TestAssets/TestObject.cs index 126a9f1..5ee381d 100644 --- a/tests/Blazored.SessionStorage.Tests/TestAssets/TestObject.cs +++ b/tests/Blazored.SessionStorage.Tests/TestAssets/TestObject.cs @@ -1,15 +1,14 @@ -namespace Blazored.SessionStorage.Tests.TestAssets +namespace Blazored.SessionStorage.Tests.TestAssets; + +public class TestObject { - public class TestObject - { - public int Id { get; set; } - public string Name { get; set; } + public int Id { get; set; } + public string Name { get; set; } - public TestObject() { } - public TestObject(int id, string name) - { - Id = id; - Name = name; - } + public TestObject() { } + public TestObject(int id, string name) + { + Id = id; + Name = name; } -} +} \ No newline at end of file From 7772060bd6d3a5f5e242067ee32dd81ff4635758 Mon Sep 17 00:00:00 2001 From: Ricardo Ferreira Date: Mon, 14 Aug 2023 16:27:38 +0100 Subject: [PATCH 20/20] Added public setter for System.Text.Json JsonSerialize --- .../StorageOptions/SessionStorageOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs b/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs index 2156750..727545b 100644 --- a/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs +++ b/src/Blazored.SessionStorage/StorageOptions/SessionStorageOptions.cs @@ -4,5 +4,5 @@ namespace Blazored.SessionStorage.StorageOptions; public class SessionStorageOptions { - public JsonSerializerOptions JsonSerializerOptions { get; } = new(); + public JsonSerializerOptions JsonSerializerOptions { get; set; } = new(); }