Skip to content

Commit

Permalink
Merge pull request #65 from sveinungf/dev/named-cell-styles
Browse files Browse the repository at this point in the history
Named cell styles
  • Loading branch information
sveinungf authored Aug 11, 2024
2 parents 6bfdb87 + a7af7ae commit 336e131
Show file tree
Hide file tree
Showing 28 changed files with 1,248 additions and 557 deletions.
16 changes: 16 additions & 0 deletions SpreadCheetah.Test/Tests/AlignmentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using SpreadCheetah.Styling;

namespace SpreadCheetah.Test.Tests;

public class AlignmentTests
{
[Fact]
public void Alignment_Indent_InvalidValue()
{
// Arrange
var alignment = new Alignment();

// Act & Assert
Assert.Throws<ArgumentOutOfRangeException>(() => alignment.Indent = -1);
}
}
269 changes: 269 additions & 0 deletions SpreadCheetah.Test/Tests/NamedStyleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
using OfficeOpenXml;
using SpreadCheetah.Styling;
using SpreadCheetah.Test.Helpers;
using System.Drawing;

namespace SpreadCheetah.Test.Tests;

public class NamedStyleTests
{
private static readonly SpreadCheetahOptions SpreadCheetahOptions = new() { BufferSize = SpreadCheetahOptions.MinimumBufferSize };

[Theory]
[InlineData(StyleNameVisibility.Visible)]
[InlineData(StyleNameVisibility.Hidden)]
public async Task Spreadsheet_AddStyle_NamedStyle(StyleNameVisibility visibility)
{
// Arrange
using var stream = new MemoryStream();
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, SpreadCheetahOptions);
await spreadsheet.StartWorksheetAsync("My sheet");
var style = new Style { Font = { Bold = true } };
const string name = "My bold style";

// Act
var addedStyleId = spreadsheet.AddStyle(style, name, visibility);
var returnedStyleId = spreadsheet.GetStyleId(name);
await spreadsheet.FinishAsync();

// Assert
Assert.Equal(addedStyleId, returnedStyleId);
SpreadsheetAssert.Valid(stream);
using var package = new ExcelPackage(stream);
var namedStyles = package.Workbook.Styles.NamedStyles;
var namedStyle = Assert.Single(namedStyles, x => !x.Name.Equals("Normal", StringComparison.Ordinal));
Assert.Equal(name, namedStyle.Name);
Assert.True(namedStyle.Style.Font.Bold);
}

[Fact]
public async Task Spreadsheet_AddStyle_NamedStyleWithNoVisiblity()
{
// Arrange
using var stream = new MemoryStream();
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, SpreadCheetahOptions);
await spreadsheet.StartWorksheetAsync("My sheet");
var style = new Style { Font = { Bold = true } };
const string name = "My bold style";

// Act
var addedStyleId = spreadsheet.AddStyle(style, name);
var returnedStyleId = spreadsheet.GetStyleId(name);
await spreadsheet.FinishAsync();

// Assert
Assert.Equal(addedStyleId, returnedStyleId);
SpreadsheetAssert.Valid(stream);
using var package = new ExcelPackage(stream);
var namedStyle = Assert.Single(package.Workbook.Styles.NamedStyles);
Assert.Equal("Normal", namedStyle.Name);
Assert.False(namedStyle.Style.Font.Bold);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Spreadsheet_AddStyle_NamedStyleUsedByCell(bool withDefaultDateTimeFormat)
{
// Arrange
using var stream = new MemoryStream();
var options = new SpreadCheetahOptions { BufferSize = SpreadCheetahOptions.MinimumBufferSize };
if (!withDefaultDateTimeFormat)
options.DefaultDateTimeFormat = null;

await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options);
await spreadsheet.StartWorksheetAsync("My sheet");
var style = new Style { Font = { Bold = true } };
const string name = "My bold style";

// Act
var styleId = spreadsheet.AddStyle(style, name, StyleNameVisibility.Visible);
await spreadsheet.AddRowAsync([new StyledCell("My cell", styleId)]);
await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
using var package = new ExcelPackage(stream);
var worksheet = Assert.Single(package.Workbook.Worksheets);
var actualCell = Assert.Single(worksheet.Cells);
Assert.Equal(name, actualCell.StyleName);
Assert.True(actualCell.Style.Font.Bold);
}

[Fact]
public async Task Spreadsheet_AddStyle_NamedStyleUsedByMultipleCells()
{
// Arrange
using var stream = new MemoryStream();
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, SpreadCheetahOptions);
var style = new Style { Font = { Bold = true } };
const string name = "My bold style";

// Act
var styleId = spreadsheet.AddStyle(style, name, StyleNameVisibility.Visible);
await spreadsheet.StartWorksheetAsync("Sheet 1");
await spreadsheet.AddRowAsync([new StyledCell("A1", styleId), new StyledCell(2, null), new StyledCell(3, styleId)]);
await spreadsheet.AddRowAsync([new StyledCell("A2", styleId)]);
await spreadsheet.StartWorksheetAsync("Sheet 2");
await spreadsheet.AddRowAsync([new StyledCell("A1", styleId)]);
await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
using var package = new ExcelPackage(stream);
var worksheet1 = package.Workbook.Worksheets["Sheet 1"];
Assert.Equal(name, worksheet1.Cells["A1"].StyleName);
Assert.NotEqual(name, worksheet1.Cells["B2"].StyleName);
Assert.Equal(name, worksheet1.Cells["C1"].StyleName);
Assert.Equal(name, worksheet1.Cells["A2"].StyleName);

var worksheet2 = package.Workbook.Worksheets["Sheet 2"];
Assert.Equal(name, worksheet2.Cells["A1"].StyleName);
}

[Fact]
public async Task Spreadsheet_AddStyle_MultipleNamedStylesUsedByMultipleCells()
{
// Arrange
using var stream = new MemoryStream();
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, SpreadCheetahOptions);
(string Name, Style Style)[] styles =
[
("Bold", new Style { Font = { Bold = true } }),
("Italic", new Style { Font = { Italic = true } }),
("Red", new Style { Fill = { Color = Color.Red } }),
("Blue", new Style { Fill = { Color = Color.Blue } })
];

// Act
var styleIds = styles.Select(x => spreadsheet.AddStyle(x.Style, x.Name, StyleNameVisibility.Visible)).ToList();

await spreadsheet.StartWorksheetAsync("Sheet 1");
await spreadsheet.AddRowAsync([new StyledCell("A1", styleIds[0]), new StyledCell(2, null), new StyledCell(3, styleIds[1])]);
await spreadsheet.AddRowAsync([new StyledCell("A2", styleIds[2])]);
await spreadsheet.StartWorksheetAsync("Sheet 2");
await spreadsheet.AddRowAsync([new StyledCell("A1", styleIds[3])]);
await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
using var package = new ExcelPackage(stream);
var worksheet1 = package.Workbook.Worksheets["Sheet 1"];
Assert.Equal(styles[0].Name, worksheet1.Cells["A1"].StyleName);
Assert.Equal(styles[1].Name, worksheet1.Cells["C1"].StyleName);
Assert.Equal(styles[2].Name, worksheet1.Cells["A2"].StyleName);

var worksheet2 = package.Workbook.Worksheets["Sheet 2"];
Assert.Equal(styles[3].Name, worksheet2.Cells["A1"].StyleName);
}

[Fact]
public async Task Spreadsheet_AddStyle_NamedStyleWithInvalidVisibility()
{
// Arrange
await using var spreadsheet = await Spreadsheet.CreateNewAsync(Stream.Null, SpreadCheetahOptions);
var style = new Style { Font = { Bold = true } };

// Act & Assert
Assert.Throws<ArgumentOutOfRangeException>(() => spreadsheet.AddStyle(style, "Name", (StyleNameVisibility)3));
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData(" Style")]
[InlineData("Style ")]
[InlineData("Normal")]
public async Task Spreadsheet_AddStyle_NamedStyleWithInvalidName(string? name)
{
// Arrange
await using var spreadsheet = await Spreadsheet.CreateNewAsync(Stream.Null, SpreadCheetahOptions);
var style = new Style { Font = { Bold = true } };

// Act & Assert
Assert.ThrowsAny<ArgumentException>(() => spreadsheet.AddStyle(style, name!));
}

[Fact]
public async Task Spreadsheet_AddStyle_NamedStyleWithTooLongName()
{
// Arrange
await using var spreadsheet = await Spreadsheet.CreateNewAsync(Stream.Null, SpreadCheetahOptions);
var style = new Style { Font = { Bold = true } };
var name = new string('c', 256);

// Act & Assert
Assert.Throws<ArgumentException>(() => spreadsheet.AddStyle(style, name));
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task Spreadsheet_AddStyle_NamedStyleWithDuplicateName(bool differentCasing)
{
// Arrange
await using var spreadsheet = await Spreadsheet.CreateNewAsync(Stream.Null, SpreadCheetahOptions);

var style = new Style { Font = { Bold = true } };
const string name = "My bold style";
spreadsheet.AddStyle(style, name);

var otherStyle = new Style { Font = { Italic = true } };
var duplicateName = differentCasing ? name.ToUpperInvariant() : name;

// Act & Assert
Assert.Throws<ArgumentException>(() => spreadsheet.AddStyle(otherStyle, duplicateName));
}

[Fact]
public async Task Spreadsheet_AddStyle_DuplicateNamedStylesReturnsDifferentStyleIds()
{
// Arrange
await using var spreadsheet = await Spreadsheet.CreateNewAsync(Stream.Null, SpreadCheetahOptions);
var style1 = new Style { Font = { Bold = true } };
var style2 = style1 with { };

// Act
var styleId1 = spreadsheet.AddStyle(style1, "Style 1");
var styleId2 = spreadsheet.AddStyle(style2, "Style 2");

// Assert
Assert.NotEqual(styleId1.Id, styleId2.Id);
}

[Fact]
public async Task Spreadsheet_AddStyle_NamedStyleDuplicateOfUnnamedStyleReturnsDifferentStyleIds()
{
// Arrange
await using var spreadsheet = await Spreadsheet.CreateNewAsync(Stream.Null, SpreadCheetahOptions);
var style1 = new Style { Font = { Bold = true } };
var style2 = style1 with { };

// Act
var styleId1 = spreadsheet.AddStyle(style1);
var styleId2 = spreadsheet.AddStyle(style2, "Style 2");

// Assert
Assert.NotEqual(styleId1.Id, styleId2.Id);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task Spreadsheet_GetStyleId_IncorrectName(bool existingNamedStyle)
{
// Arrange
await using var spreadsheet = await Spreadsheet.CreateNewAsync(Stream.Null, SpreadCheetahOptions);

if (existingNamedStyle)
{
var style = new Style { Font = { Bold = true } };
const string name = "My bold style";
spreadsheet.AddStyle(style, name);
}

// Act & Assert
Assert.Throws<SpreadCheetahException>(() => spreadsheet.GetStyleId("Other style"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ namespace SpreadCheetah
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.DataCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.StyledCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style, string name, SpreadCheetah.Styling.StyleNameVisibility? nameVisibility = default) { }
public void Dispose() { }
public System.Threading.Tasks.ValueTask DisposeAsync() { }
public System.Threading.Tasks.ValueTask<SpreadCheetah.Images.EmbeddedImage> EmbedImageAsync(System.IO.Stream stream, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask FinishAsync(System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId GetStyleId(string name) { }
public void MergeCells(string cellRange) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync(string name, SpreadCheetah.Worksheets.WorksheetOptions? options = null, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync<T>(string name, SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo<T> typeInfo, System.Threading.CancellationToken token = default) { }
Expand Down Expand Up @@ -400,6 +402,11 @@ namespace SpreadCheetah.Styling
{
public int Id { get; }
}
public enum StyleNameVisibility
{
Visible = 0,
Hidden = 1,
}
public enum VerticalAlignment
{
Bottom = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ namespace SpreadCheetah
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.DataCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.StyledCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style, string name, SpreadCheetah.Styling.StyleNameVisibility? nameVisibility = default) { }
public void Dispose() { }
public System.Threading.Tasks.ValueTask DisposeAsync() { }
public System.Threading.Tasks.ValueTask<SpreadCheetah.Images.EmbeddedImage> EmbedImageAsync(System.IO.Stream stream, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask FinishAsync(System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId GetStyleId(string name) { }
public void MergeCells(string cellRange) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync(string name, SpreadCheetah.Worksheets.WorksheetOptions? options = null, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync<T>(string name, SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo<T> typeInfo, System.Threading.CancellationToken token = default) { }
Expand Down Expand Up @@ -400,6 +402,11 @@ namespace SpreadCheetah.Styling
{
public int Id { get; }
}
public enum StyleNameVisibility
{
Visible = 0,
Hidden = 1,
}
public enum VerticalAlignment
{
Bottom = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ namespace SpreadCheetah
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.DataCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.StyledCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style, string name, SpreadCheetah.Styling.StyleNameVisibility? nameVisibility = default) { }
public void Dispose() { }
public System.Threading.Tasks.ValueTask DisposeAsync() { }
public System.Threading.Tasks.ValueTask<SpreadCheetah.Images.EmbeddedImage> EmbedImageAsync(System.IO.Stream stream, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask FinishAsync(System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId GetStyleId(string name) { }
public void MergeCells(string cellRange) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync(string name, SpreadCheetah.Worksheets.WorksheetOptions? options = null, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync<T>(string name, SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo<T> typeInfo, System.Threading.CancellationToken token = default) { }
Expand Down Expand Up @@ -400,6 +402,11 @@ namespace SpreadCheetah.Styling
{
public int Id { get; }
}
public enum StyleNameVisibility
{
Visible = 0,
Hidden = 1,
}
public enum VerticalAlignment
{
Bottom = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@ namespace SpreadCheetah
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.DataCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory<SpreadCheetah.StyledCell> cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style) { }
public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style, string name, SpreadCheetah.Styling.StyleNameVisibility? nameVisibility = default) { }
public void Dispose() { }
public System.Threading.Tasks.ValueTask DisposeAsync() { }
public System.Threading.Tasks.ValueTask<SpreadCheetah.Images.EmbeddedImage> EmbedImageAsync(System.IO.Stream stream, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask FinishAsync(System.Threading.CancellationToken token = default) { }
public SpreadCheetah.Styling.StyleId GetStyleId(string name) { }
public void MergeCells(string cellRange) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync(string name, SpreadCheetah.Worksheets.WorksheetOptions? options = null, System.Threading.CancellationToken token = default) { }
public System.Threading.Tasks.ValueTask StartWorksheetAsync<T>(string name, SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo<T> typeInfo, System.Threading.CancellationToken token = default) { }
Expand Down Expand Up @@ -399,6 +401,11 @@ namespace SpreadCheetah.Styling
{
public int Id { get; }
}
public enum StyleNameVisibility
{
Visible = 0,
Hidden = 1,
}
public enum VerticalAlignment
{
Bottom = 0,
Expand Down
Loading

0 comments on commit 336e131

Please sign in to comment.