diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 255dcbb8d..d9dca4e59 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -22,7 +22,7 @@ jobs: run: chmod u+x build.sh - name: Build and test working-directory: ./ - run: ./build.sh runtests + run: ./build.sh runtestsallnonetfx build-and-test-windows: @@ -36,4 +36,4 @@ jobs: dotnet-version: 6.x.x - name: Build and test (includes netfx) working-directory: ./ - run: ./build.cmd runtestsnetfx + run: ./build.cmd runtestsall diff --git a/Plotly.NET.Core.sln b/Plotly.NET.Core.sln new file mode 100644 index 000000000..c31beb505 --- /dev/null +++ b/Plotly.NET.Core.sln @@ -0,0 +1,53 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33530.505 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET", "src\Plotly.NET\Plotly.NET.fsproj", "{0CF51626-E5F1-453E-A689-2D95B436D96B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{61781406-92F2-4717-AB99-C67C02D4A112}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .config\dotnet-tools.json = .config\dotnet-tools.json + key.snk = key.snk + LICENSE = LICENSE + README.md = README.md + RELEASE_NOTES.md = RELEASE_NOTES.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{2FF92462-3027-47B2-BB69-0ADD97F11943}" + ProjectSection(SolutionItems) = preProject + build.cmd = build.cmd + build.sh = build.sh + global.json = global.json + EndProjectSection +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "build", "build\build.fsproj", "{3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Dotnet|Any CPU = Dotnet|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0CF51626-E5F1-453E-A689-2D95B436D96B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CF51626-E5F1-453E-A689-2D95B436D96B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CF51626-E5F1-453E-A689-2D95B436D96B}.Dotnet|Any CPU.ActiveCfg = Dotnet|Any CPU + {0CF51626-E5F1-453E-A689-2D95B436D96B}.Dotnet|Any CPU.Build.0 = Dotnet|Any CPU + {0CF51626-E5F1-453E-A689-2D95B436D96B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CF51626-E5F1-453E-A689-2D95B436D96B}.Release|Any CPU.Build.0 = Release|Any CPU + {3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3356D9F4-4BCF-47D7-A9BB-CCB998E37B1D} = {2FF92462-3027-47B2-BB69-0ADD97F11943} + EndGlobalSection +EndGlobal diff --git a/Plotly.NET.TestSuite.sln b/Plotly.NET.TestSuite.sln new file mode 100644 index 000000000..1a4e31a3a --- /dev/null +++ b/Plotly.NET.TestSuite.sln @@ -0,0 +1,83 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33530.505 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{CAAD0310-031B-4875-9AB2-7026A0DCD941}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CoreTests", "CoreTests", "{6C49B0AB-8502-4E46-9348-64161F84E727}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExtensionLibTests", "ExtensionLibTests", "{CCB58D28-C02B-49A8-8614-8BD3D4FA9DF9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JSTests", "JSTests", "{82F7B528-07C1-4E77-AD8B-9738FAB57974}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharpTestBase", "tests\Common\FSharpTestBase\FSharpTestBase.fsproj", "{1971920D-8E65-41FF-84DA-47E8B29738E8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpTestBase", "tests\Common\CSharpTestBase\CSharpTestBase.csproj", "{21AFCC90-CB61-4B03-9394-D9A9A769AB6E}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "CoreTests", "tests\CoreTests\CoreTests\CoreTests.fsproj", "{BA47F7BE-B699-4E65-9CD0-938F92BA976C}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "StrongNameTests", "tests\CoreTests\StrongNameTests\StrongNameTests.fsproj", "{26AA07CD-168B-45FC-93D3-315A26198E92}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpInteroperabilityTests", "tests\CoreTests\CSharpInteroperabilityTests\CSharpInteroperabilityTests.csproj", "{FEAB6AB7-1F9A-4E75-B047-F34C3AE1919B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpTests", "tests\ExtensionLibsTests\CSharpTests\CSharpTests.csproj", "{24AE6CF2-8A86-4F8B-82D6-30D04D674933}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ImageExportTests", "tests\ExtensionLibsTests\ImageExportTests\ImageExportTests.fsproj", "{B1214EE4-C1D4-4046-975D-A06373390E71}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".project", ".project", "{F8584D5A-AC8A-488D-9ACF-7C6570A9BE2B}" + ProjectSection(SolutionItems) = preProject + tests\README.md = tests\README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1971920D-8E65-41FF-84DA-47E8B29738E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1971920D-8E65-41FF-84DA-47E8B29738E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1971920D-8E65-41FF-84DA-47E8B29738E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1971920D-8E65-41FF-84DA-47E8B29738E8}.Release|Any CPU.Build.0 = Release|Any CPU + {21AFCC90-CB61-4B03-9394-D9A9A769AB6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21AFCC90-CB61-4B03-9394-D9A9A769AB6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21AFCC90-CB61-4B03-9394-D9A9A769AB6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21AFCC90-CB61-4B03-9394-D9A9A769AB6E}.Release|Any CPU.Build.0 = Release|Any CPU + {BA47F7BE-B699-4E65-9CD0-938F92BA976C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA47F7BE-B699-4E65-9CD0-938F92BA976C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA47F7BE-B699-4E65-9CD0-938F92BA976C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA47F7BE-B699-4E65-9CD0-938F92BA976C}.Release|Any CPU.Build.0 = Release|Any CPU + {26AA07CD-168B-45FC-93D3-315A26198E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26AA07CD-168B-45FC-93D3-315A26198E92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26AA07CD-168B-45FC-93D3-315A26198E92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26AA07CD-168B-45FC-93D3-315A26198E92}.Release|Any CPU.Build.0 = Release|Any CPU + {FEAB6AB7-1F9A-4E75-B047-F34C3AE1919B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEAB6AB7-1F9A-4E75-B047-F34C3AE1919B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEAB6AB7-1F9A-4E75-B047-F34C3AE1919B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEAB6AB7-1F9A-4E75-B047-F34C3AE1919B}.Release|Any CPU.Build.0 = Release|Any CPU + {24AE6CF2-8A86-4F8B-82D6-30D04D674933}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24AE6CF2-8A86-4F8B-82D6-30D04D674933}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24AE6CF2-8A86-4F8B-82D6-30D04D674933}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24AE6CF2-8A86-4F8B-82D6-30D04D674933}.Release|Any CPU.Build.0 = Release|Any CPU + {B1214EE4-C1D4-4046-975D-A06373390E71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1214EE4-C1D4-4046-975D-A06373390E71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1214EE4-C1D4-4046-975D-A06373390E71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1214EE4-C1D4-4046-975D-A06373390E71}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1971920D-8E65-41FF-84DA-47E8B29738E8} = {CAAD0310-031B-4875-9AB2-7026A0DCD941} + {21AFCC90-CB61-4B03-9394-D9A9A769AB6E} = {CAAD0310-031B-4875-9AB2-7026A0DCD941} + {BA47F7BE-B699-4E65-9CD0-938F92BA976C} = {6C49B0AB-8502-4E46-9348-64161F84E727} + {26AA07CD-168B-45FC-93D3-315A26198E92} = {6C49B0AB-8502-4E46-9348-64161F84E727} + {FEAB6AB7-1F9A-4E75-B047-F34C3AE1919B} = {6C49B0AB-8502-4E46-9348-64161F84E727} + {24AE6CF2-8A86-4F8B-82D6-30D04D674933} = {CCB58D28-C02B-49A8-8614-8BD3D4FA9DF9} + {B1214EE4-C1D4-4046-975D-A06373390E71} = {CCB58D28-C02B-49A8-8614-8BD3D4FA9DF9} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {67F9FFDF-3841-4DE1-9A55-9494FFC0A288} + EndGlobalSection +EndGlobal diff --git a/Plotly.NET.sln b/Plotly.NET.sln index 4537c0c2b..bc2e9fbfb 100644 --- a/Plotly.NET.sln +++ b/Plotly.NET.sln @@ -28,8 +28,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7C6D08E7 EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET", "src\Plotly.NET\Plotly.NET.fsproj", "{DFAC135B-36B8-4347-B1DD-B5E0144610C2}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET.Tests", "tests\Plotly.NET.Tests\Plotly.NET.Tests.fsproj", "{2C9916F4-817A-4B70-8D83-F48E9A30544F}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".ci", ".ci", "{2461AFBF-6E10-4F7B-A0EA-3D62541C2EB1}" ProjectSection(SolutionItems) = preProject .github\workflows\build-and-deploy-docs.yml = .github\workflows\build-and-deploy-docs.yml @@ -139,24 +137,40 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0E87E47E-9ED EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EAE25A1F-86FC-426B-803F-1006D1AD06A8}" ProjectSection(SolutionItems) = preProject - src\Plotly.NET\Playground.fsx = src\Plotly.NET\Playground.fsx + tests\README.md = tests\README.md EndProjectSection EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET.Tests.FSharpConsole", "tests\Plotly.NET.Tests.FSharpConsole\Plotly.NET.Tests.FSharpConsole.fsproj", "{60114ACE-77E6-4A19-9A2F-CB64084174AF}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "build", "build\build.fsproj", "{403785C9-B5B1-4BA4-9944-A0F9D5D1B3CD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plotly.NET.Tests.CSharpConsole", "tests\Plotly.NET.Tests.CSharpConsole\Plotly.NET.Tests.CSharpConsole.csproj", "{1BC73DA0-586F-45C2-BC5B-A70C452A00F0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plotly.NET.CSharp", "src\Plotly.NET.CSharp\Plotly.NET.CSharp.csproj", "{F944FE69-F4A5-4B48-8E4D-BE4B61E92B26}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET.ImageExport.Tests", "tests\Plotly.NET.ImageExport.Tests\Plotly.NET.ImageExport.Tests.fsproj", "{55A461C3-8018-4020-B16E-D6005BDFCAED}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CoreTests", "CoreTests", "{023425D8-B375-45ED-A29A-A3ED00C0E2BC}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "build", "build\build.fsproj", "{403785C9-B5B1-4BA4-9944-A0F9D5D1B3CD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JSTests", "JSTests", "{94E71E92-C244-4AF1-903A-DAC5448DADA5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plotly.NET.CSharp", "src\Plotly.NET.CSharp\Plotly.NET.CSharp.csproj", "{F944FE69-F4A5-4B48-8E4D-BE4B61E92B26}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExtensionLibTests", "ExtensionLibTests", "{02886FBB-DB32-4BBB-A93C-E13EBF453ACC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ConsoleApps", "ConsoleApps", "{E8897848-2EF4-4126-9315-ADE0BA92D951}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpConsole", "tests\ConsoleApps\CSharpConsole\CSharpConsole.csproj", "{029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharpConsole", "tests\ConsoleApps\FSharpConsole\FSharpConsole.fsproj", "{598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "CoreTests", "tests\CoreTests\CoreTests\CoreTests.fsproj", "{DA7BC908-7DC9-4BA7-8100-17D3FD2901EA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpInteroperabilityTests", "tests\CoreTests\CSharpInteroperabilityTests\CSharpInteroperabilityTests.csproj", "{C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "StrongNameTests", "tests\CoreTests\StrongNameTests\StrongNameTests.fsproj", "{32E187BB-D1D4-4864-A021-55A7A08E17EC}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ImageExportTests", "tests\ExtensionLibsTests\ImageExportTests\ImageExportTests.fsproj", "{A3693DD1-4D34-4B10-B310-07A4C7433F7D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpTests", "tests\ExtensionLibsTests\CSharpTests\CSharpTests.csproj", "{1170E3A9-D83E-40B0-8D51-20485C76BE88}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plotly.NET.CSharp.Tests", "tests\Plotly.NET.CSharp.Tests\Plotly.NET.CSharp.Tests.csproj", "{4C24BA53-F41C-4110-AD7A-28143DCF671E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{6CFE1FBD-0999-4704-9F79-8A3D31580477}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plotly.NET.Tests.CSharpInteroperability", "tests\Plotly.NET.Tests.CSharpInteroperability\Plotly.NET.Tests.CSharpInteroperability.csproj", "{64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpTestBase", "tests\Common\CSharpTestBase\CSharpTestBase.csproj", "{9B73CE85-9F85-42DB-8CEC-7A49B70189ED}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET.Tests.StrongName", "tests\Plotly.NET.Tests.StrongName\Plotly.NET.Tests.StrongName.fsproj", "{E923A457-A6D3-4614-A8A9-287E4A4797EF}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharpTestBase", "tests\Common\FSharpTestBase\FSharpTestBase.fsproj", "{18F778B2-3409-4309-8C9B-596109072481}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -171,12 +185,6 @@ Global {DFAC135B-36B8-4347-B1DD-B5E0144610C2}.Dotnet|Any CPU.Build.0 = Dotnet|Any CPU {DFAC135B-36B8-4347-B1DD-B5E0144610C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFAC135B-36B8-4347-B1DD-B5E0144610C2}.Release|Any CPU.Build.0 = Release|Any CPU - {2C9916F4-817A-4B70-8D83-F48E9A30544F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2C9916F4-817A-4B70-8D83-F48E9A30544F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2C9916F4-817A-4B70-8D83-F48E9A30544F}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU - {2C9916F4-817A-4B70-8D83-F48E9A30544F}.Dotnet|Any CPU.Build.0 = Debug|Any CPU - {2C9916F4-817A-4B70-8D83-F48E9A30544F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2C9916F4-817A-4B70-8D83-F48E9A30544F}.Release|Any CPU.Build.0 = Release|Any CPU {0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU @@ -189,24 +197,6 @@ Global {6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Dotnet|Any CPU.Build.0 = Debug|Any CPU {6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Release|Any CPU.ActiveCfg = Release|Any CPU {6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Release|Any CPU.Build.0 = Release|Any CPU - {60114ACE-77E6-4A19-9A2F-CB64084174AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {60114ACE-77E6-4A19-9A2F-CB64084174AF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60114ACE-77E6-4A19-9A2F-CB64084174AF}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU - {60114ACE-77E6-4A19-9A2F-CB64084174AF}.Dotnet|Any CPU.Build.0 = Debug|Any CPU - {60114ACE-77E6-4A19-9A2F-CB64084174AF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {60114ACE-77E6-4A19-9A2F-CB64084174AF}.Release|Any CPU.Build.0 = Release|Any CPU - {1BC73DA0-586F-45C2-BC5B-A70C452A00F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1BC73DA0-586F-45C2-BC5B-A70C452A00F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1BC73DA0-586F-45C2-BC5B-A70C452A00F0}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU - {1BC73DA0-586F-45C2-BC5B-A70C452A00F0}.Dotnet|Any CPU.Build.0 = Debug|Any CPU - {1BC73DA0-586F-45C2-BC5B-A70C452A00F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1BC73DA0-586F-45C2-BC5B-A70C452A00F0}.Release|Any CPU.Build.0 = Release|Any CPU - {55A461C3-8018-4020-B16E-D6005BDFCAED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55A461C3-8018-4020-B16E-D6005BDFCAED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55A461C3-8018-4020-B16E-D6005BDFCAED}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU - {55A461C3-8018-4020-B16E-D6005BDFCAED}.Dotnet|Any CPU.Build.0 = Debug|Any CPU - {55A461C3-8018-4020-B16E-D6005BDFCAED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55A461C3-8018-4020-B16E-D6005BDFCAED}.Release|Any CPU.Build.0 = Release|Any CPU {403785C9-B5B1-4BA4-9944-A0F9D5D1B3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {403785C9-B5B1-4BA4-9944-A0F9D5D1B3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {403785C9-B5B1-4BA4-9944-A0F9D5D1B3CD}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU @@ -219,44 +209,87 @@ Global {F944FE69-F4A5-4B48-8E4D-BE4B61E92B26}.Dotnet|Any CPU.Build.0 = Debug|Any CPU {F944FE69-F4A5-4B48-8E4D-BE4B61E92B26}.Release|Any CPU.ActiveCfg = Release|Any CPU {F944FE69-F4A5-4B48-8E4D-BE4B61E92B26}.Release|Any CPU.Build.0 = Release|Any CPU - {4C24BA53-F41C-4110-AD7A-28143DCF671E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C24BA53-F41C-4110-AD7A-28143DCF671E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C24BA53-F41C-4110-AD7A-28143DCF671E}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU - {4C24BA53-F41C-4110-AD7A-28143DCF671E}.Dotnet|Any CPU.Build.0 = Debug|Any CPU - {4C24BA53-F41C-4110-AD7A-28143DCF671E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C24BA53-F41C-4110-AD7A-28143DCF671E}.Release|Any CPU.Build.0 = Release|Any CPU - {64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU - {64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB}.Dotnet|Any CPU.Build.0 = Debug|Any CPU - {64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB}.Release|Any CPU.Build.0 = Release|Any CPU - {E923A457-A6D3-4614-A8A9-287E4A4797EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E923A457-A6D3-4614-A8A9-287E4A4797EF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E923A457-A6D3-4614-A8A9-287E4A4797EF}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU - {E923A457-A6D3-4614-A8A9-287E4A4797EF}.Dotnet|Any CPU.Build.0 = Debug|Any CPU - {E923A457-A6D3-4614-A8A9-287E4A4797EF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E923A457-A6D3-4614-A8A9-287E4A4797EF}.Release|Any CPU.Build.0 = Release|Any CPU + {029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6}.Release|Any CPU.Build.0 = Release|Any CPU + {598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C}.Release|Any CPU.Build.0 = Release|Any CPU + {DA7BC908-7DC9-4BA7-8100-17D3FD2901EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA7BC908-7DC9-4BA7-8100-17D3FD2901EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA7BC908-7DC9-4BA7-8100-17D3FD2901EA}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {DA7BC908-7DC9-4BA7-8100-17D3FD2901EA}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {DA7BC908-7DC9-4BA7-8100-17D3FD2901EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA7BC908-7DC9-4BA7-8100-17D3FD2901EA}.Release|Any CPU.Build.0 = Release|Any CPU + {C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1}.Release|Any CPU.Build.0 = Release|Any CPU + {32E187BB-D1D4-4864-A021-55A7A08E17EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32E187BB-D1D4-4864-A021-55A7A08E17EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32E187BB-D1D4-4864-A021-55A7A08E17EC}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {32E187BB-D1D4-4864-A021-55A7A08E17EC}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {32E187BB-D1D4-4864-A021-55A7A08E17EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32E187BB-D1D4-4864-A021-55A7A08E17EC}.Release|Any CPU.Build.0 = Release|Any CPU + {A3693DD1-4D34-4B10-B310-07A4C7433F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3693DD1-4D34-4B10-B310-07A4C7433F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3693DD1-4D34-4B10-B310-07A4C7433F7D}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {A3693DD1-4D34-4B10-B310-07A4C7433F7D}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {A3693DD1-4D34-4B10-B310-07A4C7433F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3693DD1-4D34-4B10-B310-07A4C7433F7D}.Release|Any CPU.Build.0 = Release|Any CPU + {1170E3A9-D83E-40B0-8D51-20485C76BE88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1170E3A9-D83E-40B0-8D51-20485C76BE88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1170E3A9-D83E-40B0-8D51-20485C76BE88}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {1170E3A9-D83E-40B0-8D51-20485C76BE88}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {1170E3A9-D83E-40B0-8D51-20485C76BE88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1170E3A9-D83E-40B0-8D51-20485C76BE88}.Release|Any CPU.Build.0 = Release|Any CPU + {9B73CE85-9F85-42DB-8CEC-7A49B70189ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B73CE85-9F85-42DB-8CEC-7A49B70189ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B73CE85-9F85-42DB-8CEC-7A49B70189ED}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {9B73CE85-9F85-42DB-8CEC-7A49B70189ED}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {9B73CE85-9F85-42DB-8CEC-7A49B70189ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B73CE85-9F85-42DB-8CEC-7A49B70189ED}.Release|Any CPU.Build.0 = Release|Any CPU + {18F778B2-3409-4309-8C9B-596109072481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18F778B2-3409-4309-8C9B-596109072481}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18F778B2-3409-4309-8C9B-596109072481}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {18F778B2-3409-4309-8C9B-596109072481}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {18F778B2-3409-4309-8C9B-596109072481}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18F778B2-3409-4309-8C9B-596109072481}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {DFAC135B-36B8-4347-B1DD-B5E0144610C2} = {0E87E47E-9EDC-4525-AF72-F0E139D54236} - {2C9916F4-817A-4B70-8D83-F48E9A30544F} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} {0F135E3B-B0E1-42A9-B180-18C0221DC7B8} = {0E87E47E-9EDC-4525-AF72-F0E139D54236} {5219BAC7-ACE6-435F-A983-BC63DD7B745E} = {7B09CC0A-F1E1-4094-9DE4-B047581E01F0} {60FB82C0-F472-494E-BCF7-7B3C54212406} = {7B09CC0A-F1E1-4094-9DE4-B047581E01F0} {CDB973F2-0F60-4ADB-84A8-924AFA8B6D49} = {7B09CC0A-F1E1-4094-9DE4-B047581E01F0} {6CFC629E-1A0C-4EF3-8495-BA00A356A381} = {0E87E47E-9EDC-4525-AF72-F0E139D54236} - {60114ACE-77E6-4A19-9A2F-CB64084174AF} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} - {1BC73DA0-586F-45C2-BC5B-A70C452A00F0} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} - {55A461C3-8018-4020-B16E-D6005BDFCAED} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} {403785C9-B5B1-4BA4-9944-A0F9D5D1B3CD} = {7C6D08E7-3EAC-4335-8F4B-252C193C27C9} {F944FE69-F4A5-4B48-8E4D-BE4B61E92B26} = {0E87E47E-9EDC-4525-AF72-F0E139D54236} - {4C24BA53-F41C-4110-AD7A-28143DCF671E} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} - {64022D22-EB9A-4AE3-A0E6-BFA41E8B18CB} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} - {E923A457-A6D3-4614-A8A9-287E4A4797EF} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} + {023425D8-B375-45ED-A29A-A3ED00C0E2BC} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} + {94E71E92-C244-4AF1-903A-DAC5448DADA5} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} + {02886FBB-DB32-4BBB-A93C-E13EBF453ACC} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} + {E8897848-2EF4-4126-9315-ADE0BA92D951} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} + {029EBCB4-0E8B-40F8-B7AA-61B814FAE5A6} = {E8897848-2EF4-4126-9315-ADE0BA92D951} + {598AB47B-EFE7-414A-B6EA-8C96B4AA7A4C} = {E8897848-2EF4-4126-9315-ADE0BA92D951} + {DA7BC908-7DC9-4BA7-8100-17D3FD2901EA} = {023425D8-B375-45ED-A29A-A3ED00C0E2BC} + {C10C0225-FBBA-4E3C-99FD-FA4A2BCBB3B1} = {023425D8-B375-45ED-A29A-A3ED00C0E2BC} + {32E187BB-D1D4-4864-A021-55A7A08E17EC} = {023425D8-B375-45ED-A29A-A3ED00C0E2BC} + {A3693DD1-4D34-4B10-B310-07A4C7433F7D} = {02886FBB-DB32-4BBB-A93C-E13EBF453ACC} + {1170E3A9-D83E-40B0-8D51-20485C76BE88} = {02886FBB-DB32-4BBB-A93C-E13EBF453ACC} + {6CFE1FBD-0999-4704-9F79-8A3D31580477} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} + {9B73CE85-9F85-42DB-8CEC-7A49B70189ED} = {6CFE1FBD-0999-4704-9F79-8A3D31580477} + {18F778B2-3409-4309-8C9B-596109072481} = {6CFE1FBD-0999-4704-9F79-8A3D31580477} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7177F1E1-341C-48AB-9864-6B525FFF7633} diff --git a/README.md b/README.md index 894efe7ea..fd60545d9 100644 --- a/README.md +++ b/README.md @@ -5,96 +5,151 @@ [![](https://img.shields.io/nuget/vpre/Plotly.NET)](https://www.nuget.org/packages/Plotly.NET/) [![Discord](https://img.shields.io/discord/836161044501889064?color=purple&label=Join%20our%20Discord%21&logo=discord&logoColor=white)](https://discord.gg/k3kUtFY8DB) ![](https://img.shields.io/badge/supported%20plotly.js%20version-2.18.1-blue) - -We have published a software paper on Plotly.NET. If you are using Plotly.NET for your research please cite or look at : - [![DOI](https://img.shields.io/badge/DOI-10.12688%2Ff1000research.123971.1-brightgreen)](https://doi.org/10.12688/f1000research.123971.1) -Plotly.NET provides functions for generating and rendering plotly.js charts in **.NET** programming languages 📈🚀. - ### Table of contents +- [What is Plotly.NET?](#what-is-plotlynet) - [Installation](#installation) - - [For applications and libraries](#for-applications-and-libraries) - - [For scripting](#for-scripting) - - [For dotnet interactive notebooks](#for-dotnet-interactive-notebooks) - - [Nightly builds](#getting-nightly-builds) - [Documentation](#documentation) - - [Getting started](#getting-started) - - [Full library reference](#full-library-reference) -- [Develop](#develop) + - [Quick starts](#quick-starts) + - [F#](#f) + - [C#](#c) + - [Samples and tutorials](#samples-and-tutorials) + - [Full library reference](#full-library-reference) +- [Development](#development) - [build](#build) + - [running and writing tests](#running-and-writing-tests) - [docs](#docs) -- [Contributors](#contributors) -- [Library license](#library-license) + - [Contributors](#contributors) + - [Library license](#library-license) +- [FAQ](#faq) + +# What is Plotly.NET? + +Plotly.NET is an Interactive charting library for **.NET** programming languages 📈🚀. + +It is built on top of plotly.js and provides several API layers for creating, styling and rendering ✨**beatiful data visualizations**✨. + +To get a deep-dive into the rationale behind the design choices of Plotly.NET, check out our [F1000Research paper](https://doi.org/10.12688/f1000research.123971.1)! +![](docs/img/showcase.gif) + +In short, Plotly.NET consists of multiple API layers and packages: + +- `Plotly.NET` - The core API is written in F# and provides multiple API layers to create and style charts - from the high-level, type-safe `Chart` API to low-level direct chart object manipulation. It is the foundation for all other packages. + +- `Plotly.NET.Interactive` - This package provides interactive formatting extensions for .NET interactive notebooks. + +- `Plotly.NET.ImageExport` - This package provides extensions for Plotly.NET to render charts as static images programmatically. + +- `Plotly.NET.CSharp` - This package provides an idiomatic C# API. Note that you can use the core API in C#, this package just removes some friction at some places. # Installation -The most recent Plotly.NET package is [![](https://img.shields.io/nuget/vpre/Plotly.NET)](https://www.nuget.org/packages/Plotly.NET/). +You can get all Plotly.NET packages via nuget: -Plotly.NET also provides several extension packages: +| Package Name| Nuget | +|---|---| +| Plotly.NET | [![](https://img.shields.io/nuget/vpre/Plotly.NET)](https://www.nuget.org/packages/Plotly.NET/) | +| Plotly.NET.Interactive | [![](https://img.shields.io/nuget/vpre/Plotly.NET.Interactive)](https://www.nuget.org/packages/Plotly.NET.Interactive/) | +| Plotly.NET.ImageExport | [![](https://img.shields.io/nuget/vpre/Plotly.NET.ImageExport)](https://www.nuget.org/packages/Plotly.NET.ImageExport/) | +| Plotly.NET.CSharp | [![](https://img.shields.io/nuget/vpre/Plotly.NET.CSharp)](https://www.nuget.org/packages/Plotly.NET.CSharp/) | -| Package Name| Plotly.NET.Interactive | Plotly.NET.ImageExport | Plotly.NET.CSharp | -|---|---|---|---| -| Description | Interactive formatting extension for .NET interactive notebooks| programmatic static image export | idiomatic C# API. Note that you can use the core API in C#, this package just removes some friction at some places. [See bindings progress](https://github.com/plotly/Plotly.NET/issues/296)| -| Nuget | [![](https://img.shields.io/nuget/vpre/Plotly.NET.Interactive)](https://www.nuget.org/packages/Plotly.NET.Interactive/) | [![](https://img.shields.io/nuget/vpre/Plotly.NET.ImageExport)](https://www.nuget.org/packages/Plotly.NET.ImageExport/) | [![](https://img.shields.io/nuget/vpre/Plotly.NET.CSharp)](https://www.nuget.org/packages/Plotly.NET.CSharp/) | +# Documentation -### For applications and libraries +## Quick starts - - dotnet CLI +### F# -```shell -dotnet add package Plotly.NET -``` +In F# projects, just add the `Plotly.NET` package to your project and you are ready to go! - - paket CLI +
+ 📖 Polyglot Notebook 📖 Quick start -```shell -paket add Plotly.NET --version -``` +
- - package manager +To enable Plotly.NET in a polyglot notebook, reference the `Plotly.NET.Interactive` package: -```shell -Install-Package Plotly.NET -Version +```fsharp +#r "nuget: Plotly.NET.Interactive" ``` -Or add the package reference directly to your `.*proj` file: +To display a chart, just end a F# cell with it: +```fsharp +open Plotly.NET + +Chart.Point( + x = [0 .. 10], + y = [0 .. 10] +) +|> Chart.withTitle "Hello World!" ``` - -``` +![notebook quichstart in F#](docs/img/notebook_quickstart_fsharp.png) -### For scripting +
-You can include the package via an inline package reference: +
+ .fsx Scripting quickstart -``` -#r "nuget: Plotly.NET, " +To enable Plotly.NET in a .fsx script, reference the `Plotly.NET` package: + +```fsharp +#r "nuget: Plotly.NET" ``` -### For dotnet interactive notebooks +To display a chart in your browser, use the `Chart.show` function: -You can use the same inline package reference as in script, but as an additional goodie, -the interactive extensions for dotnet interactive have you covered for seamless chart rendering: +```fsharp +open Plotly.NET -``` -#r "nuget: Plotly.NET, " -#r "nuget: Plotly.NET.Interactive, " +Chart.Point( + x = [0 .. 10], + y = [0 .. 10] +) +|> Chart.withTitle "Hello World!" +|> Chart.show ```
-# Documentation +### C# + +In C# projects, just add the `Plotly.NET.CSharp` package to your project and you are ready to go! -## Getting started +
+ 📖 Polyglot Notebook 📖 Quick start -The landing page of our docs contains everything to get you started fast, check it out [📖 here](http://plotly.github.io/Plotly.NET/) +
+ +To enable Plotly.NET in a polyglot notebook for C#, reference the `Plotly.NET.Interactive` and `Plotly.NET.CSharp` packages: + +```csharp +#r "nuget: Plotly.NET.Interactive" +#r "nuget: Plotly.NET.CSharp" +``` + +To display a chart, just end a C# cell with it: + +```csharp +using Plotly.NET.CSharp; + +Chart.Point( + x: Enumerable.Range(0,11), + y: Enumerable.Range(0,11) +) +``` +![notebook quichstart in C#](docs/img/notebook_quickstart_csharp.png) +
+ +## Samples and tutorials + +You can find extensive documentation with samples and tutorials of the core `Plotly.NET` library [📖 here](http://plotly.github.io/Plotly.NET/). ## Full library reference -The API reference is available [📚 here](http://plotly.github.io/Plotly.NET/reference/index.html) +The API reference for all packages is available [📚 here](http://plotly.github.io/Plotly.NET/reference/index.html) The documentation for this library is automatically generated (using FSharp.Formatting) from *.fsx and *.md files in the docs folder. If you find a typo, please submit a pull request! @@ -112,7 +167,7 @@ Check the [build project](https://github.com/plotly/Plotly.NET/blob/dev/build) t # Build only ./build.cmd -# Full release buildchain: build, test, pack, build the docs, push a git tag, publish thze nuget package, release the docs +# Full release buildchain: build, test, pack, build the docs, push a git tag, publish the nuget package, release the docs ./build.cmd release # The same for prerelease versions: @@ -132,6 +187,10 @@ build.sh prerelease ``` +### running and writing tests + +please refer to the detailed readme in the [tests folder](./tests/README.md) + ### docs The docs are contained in `.fsx` and `.md` files in the `docs` folder. To develop docs on a local server with hot reload, run the following in the root of the project: @@ -155,3 +214,33 @@ Plotly.NET is a community maintained open source projects. Big thanks to all con ## Library license The library is available under the [MIT license](https://github.com/plotly/Plotly.NET/blob/dev/LICENSE). + +# FAQ + +
+Why are there two separate packages for C# and F#? + +
+ +These packages are not separate, Plotly.NET.CSharp is built **on top** of Plotly.NET. + +`Plotly.NET` (written in F#) is the main project. It is designed with interoperability in mind, and it is possible to use it from C#. + +Over the years, some friction between F# and C# have surfaced that cannot be overcome in the F# codebase. For more info, take a look at this issue where we discuss the topic: https://github.com/plotly/Plotly.NET/issues/285 + +The `Plotly.NET.CSharp` package (written in C#, using the F# API internally) is a thin wrapper around the core API. It is not necessary to use it, but it can make the API more idiomatic and ergonomic for C# users. +
+ +
+Can i use Plotly.NET in [insert your UI library here]? + +
+ +It depends. Plotly.NET creates JSON for consumption by the plotly.js library. The actual rendering is done by plotly.js, in an environment where javascript can be run. + +This means that your UI libs needs some way of displaying html and executing javascript to be compatible with Plotly.NET. That is true for most UI libs though, [here for example is a POC for Blazor](). + +For Windows Forms and WPF you could use a WebView control to display the charts. + +You could also use Plotly.NET.ImageExport in your backend to create static images of your charts and serve those in your UI. +
\ No newline at end of file diff --git a/build/Build.fs b/build/Build.fs index 33ac5c75d..80ad04426 100644 --- a/build/Build.fs +++ b/build/Build.fs @@ -18,6 +18,20 @@ initializeContext () open BasicTasks +//workaround for tasks created with functions not being runnable. + +//let _ = TestTasks.buildTestsAll |> ignore +//let _ = TestTasks.buildTestsCore |> ignore +//let _ = TestTasks.buildTestsNetFX |> ignore +//let _ = TestTasks.buildTestsExtensionsLibs |> ignore + +//let _ = TestTasks.runTestsAll |> ignore +//let _ = TestTasks.runTestsCore |> ignore +//let _ = TestTasks.runTestsNetFX |> ignore +//let _ = TestTasks.runTestsCoreWithNetFX |> ignore +//let _ = TestTasks.runTestsExtensionLibs |> ignore + + let sourceFiles = !! "src/Plotly.NET/**/*.fs" ++ "src/Plotly.NET.ImageExport/**/*.fs" @@ -33,7 +47,7 @@ let _release = [ clean build - runTests + runTestsAll pack buildDocs createTag @@ -49,7 +63,7 @@ let _preRelease = setPrereleaseTag clean build - runTests + runTestsAll packPrerelease buildDocsPrerelease createPrereleaseTag @@ -64,7 +78,7 @@ let _releaseNoDocs = [ clean build - runTests + runTestsAll pack createTag publishNuget @@ -78,7 +92,7 @@ let _preReleaseNoDocs = setPrereleaseTag clean build - runTests + runTestsAll packPrerelease createPrereleaseTag publishNugetPrerelease diff --git a/build/PackageTasks.fs b/build/PackageTasks.fs index f5c35e4ec..ebb9fbae7 100644 --- a/build/PackageTasks.fs +++ b/build/PackageTasks.fs @@ -11,7 +11,7 @@ open Fake.Core open Fake.DotNet open Fake.IO.Globbing.Operators -let pack = BuildTask.create "Pack" [ clean; build; runTests ] { +let pack = BuildTask.create "Pack" [ clean; build; runTestsAll ] { projects |> List.iter (fun pInfo -> if promptYesNo $"creating stable package for {pInfo.Name}{System.Environment.NewLine}\tpackage version: {pInfo.PackageVersionTag}{System.Environment.NewLine}\tassembly version: {pInfo.AssemblyVersion}{System.Environment.NewLine}\tassembly informational version: {pInfo.AssemblyInformationalVersion}{System.Environment.NewLine} OK?" then @@ -62,7 +62,7 @@ let packPrerelease = [ clean build - runTests + runTestsAll ] { projects |> List.iter (fun pInfo -> diff --git a/build/ProjectInfo.fs b/build/ProjectInfo.fs index b43010046..089644062 100644 --- a/build/ProjectInfo.fs +++ b/build/ProjectInfo.fs @@ -62,27 +62,40 @@ let pkgDir = "pkg" /// binaries are built using this configuration. let configuration = "Release" +// test base projects (need to be built first) +let FSharpTestBaseProject = ProjectInfo.create("FSharpTestBase", "tests/Common/FSharpTestBase/FSharpTestBase.fsproj") +let CSharpTestBaseProject = ProjectInfo.create("CSharpTestBase", "tests/Common/CSharpTestBase/CSharpTestBase.csproj") + +let testBaseProjects = [ + FSharpTestBaseProject + CSharpTestBaseProject +] + // test projects (.NET) -let CoreTestProject = ProjectInfo.create("Plotly.NET.Tests", "tests/Plotly.NET.Tests/Plotly.NET.Tests.fsproj") -let ImageExportTestProject = ProjectInfo.create("Plotly.NET.ImageExport.Tests", "tests/Plotly.NET.ImageExport.Tests/Plotly.NET.ImageExport.Tests.fsproj") -let CSharpInteroperabilityTestProject = ProjectInfo.create("Plotly.NET.Tests.CSharpInteroperability", "tests/Plotly.NET.Tests.CSharpInteroperability/Plotly.NET.Tests.CSharpInteroperability.csproj") -let CSharpTestProject = ProjectInfo.create("Plotly.NET.CSharp.Tests", "tests/Plotly.NET.CSharp.Tests/Plotly.NET.CSharp.Tests.csproj") - -/// contains project info about all test projects -let testProjects = - [ - CoreTestProject - ImageExportTestProject - CSharpTestProject - ] - - // test projects (.NET framework) -let StrongNameTestProject = ProjectInfo.create("Plotly.NET.Tests.StrongName", "tests/Plotly.NET.Tests.StrongName/Plotly.NET.Tests.StrongName.fsproj") - -let testProjectsNetFX = - [ - StrongNameTestProject - ] +let CoreTestProject = ProjectInfo.create("CoreTests", "tests/CoreTests/CoreTests/CoreTests.fsproj") +let CSharpInteroperabilityTestProject = ProjectInfo.create("CSharpInteroperabilityTests", "tests/CoreTests/CSharpInteroperabilityTests/CSharpInteroperabilityTests.csproj") + +/// contains project info about the core test projects +let testProjectsCore = [ + CoreTestProject + CSharpInteroperabilityTestProject +] + +let ImageExportTestProject = ProjectInfo.create("ImageExportTests", "tests/ExtensionLibsTests/ImageExportTests/ImageExportTests.fsproj") +let CSharpTestProject = ProjectInfo.create("CSharpTests", "tests/ExtensionLibsTests/CSharpTests/CSharpTests.csproj") + +/// +let testProjectsExtensionsLibs = [ + ImageExportTestProject + CSharpTestProject +] + +// test projects (.NET framework) +let StrongNameTestProject = ProjectInfo.create("StrongNameTests", "tests/CoreTests/StrongNameTests/StrongNameTests.fsproj") + +let testProjectsNetFX = [ + StrongNameTestProject +] let CoreProject = ProjectInfo.create("Plotly.NET", "src/Plotly.NET/Plotly.NET.fsproj", "src/Plotly.NET/RELEASE_NOTES.md") let InteractiveProject = ProjectInfo.create("Plotly.NET.Interactive", "src/Plotly.NET.Interactive/Plotly.NET.Interactive.fsproj", "src/Plotly.NET.Interactive/RELEASE_NOTES.md") diff --git a/build/ReleaseTasks.fs b/build/ReleaseTasks.fs index e7a9c5712..752274e2b 100644 --- a/build/ReleaseTasks.fs +++ b/build/ReleaseTasks.fs @@ -16,7 +16,7 @@ open Fake.IO open Fake.IO.Globbing.Operators let createTag = - BuildTask.create "CreateTag" [ clean; build; runTests; pack ] { + BuildTask.create "CreateTag" [ clean; build; runTestsAll; pack ] { if promptYesNo (sprintf "tagging branch with %s OK?" branchTag) then Git.Branches.tag "" branchTag Git.Branches.pushTag "" projectRepo branchTag @@ -31,7 +31,7 @@ let createPrereleaseTag = setPrereleaseTag clean build - runTests + runTestsAll packPrerelease ] { if promptYesNo (sprintf "tagging branch with %s OK?" prereleaseTag) then @@ -43,7 +43,7 @@ let createPrereleaseTag = let publishNuget = - BuildTask.create "PublishNuget" [ clean; build; runTests; pack ] { + BuildTask.create "PublishNuget" [ clean; build; runTestsAll; pack ] { let targets = (!!(sprintf "%s/*.*pkg" pkgDir)) @@ -82,7 +82,7 @@ let publishNugetPrerelease = [ clean build - runTests + runTestsAll packPrerelease ] { let targets = diff --git a/build/TestTasks.fs b/build/TestTasks.fs index e6bff428d..cd9ce618b 100644 --- a/build/TestTasks.fs +++ b/build/TestTasks.fs @@ -6,50 +6,33 @@ open Fake.DotNet open ProjectInfo open BasicTasks -let buildTests = BuildTask.create "BuildTests" [clean; build] { - testProjects - |> List.iter (fun pInfo -> - let proj = pInfo.ProjFile - proj - |> DotNet.build (fun p -> - p - |> DotNet.Options.withCustomParams (Some "--no-dependencies") +let createTestBuildTask (name: string) (deps: BuildTask.TaskInfo list) (projects: ProjectInfo list) = + BuildTask.create name deps { + projects + |> List.iter (fun pInfo -> + let proj = pInfo.ProjFile + proj + |> DotNet.build (fun p -> + p + |> DotNet.Options.withCustomParams (Some "--no-dependencies") + ) ) - ) -} + } -let buildTestsNetFX = BuildTask.create "BuildTestsNetFX" [clean; build] { - testProjectsNetFX - |> List.iter (fun pInfo -> - let proj = pInfo.ProjFile - proj - |> DotNet.build (fun p -> - p - |> DotNet.Options.withCustomParams (Some "--no-dependencies") - ) - ) -} +let buildTestsAll = createTestBuildTask "BuildTestsAll" [clean; build] (testBaseProjects @ testProjectsCore @ testProjectsExtensionsLibs @ testProjectsNetFX) -/// runs the individual test projects via `dotnet test` -let runTests = - BuildTask.create "RunTests" [ clean; build; buildTests ] { - testProjects - |> Seq.iter (fun testProjectInfo -> - Fake.DotNet.DotNet.test - (fun testParams -> - { testParams with - Logger = Some "console;verbosity=detailed" - Configuration = DotNet.BuildConfiguration.fromString configuration - NoBuild = true - }) - testProjectInfo.ProjFile) - } +let buildTestsAllNoNetFX = createTestBuildTask "BuildTestsAllNoNetFX" [clean; build] (testBaseProjects @ testProjectsCore @ testProjectsExtensionsLibs) + +let buildTestsCore = createTestBuildTask "BuildTestsCore" [clean; build] (testBaseProjects @ testProjectsCore) + +let buildTestsNetFX = createTestBuildTask "BuildTestsNetFX" [clean; build] (testBaseProjects @ testProjectsNetFX) + +let buildTestsExtensionsLibs = createTestBuildTask "BuildTestsExtensionsLibs" [clean; build] (testBaseProjects @ testProjectsExtensionsLibs) -/// runs the individual test projects via `dotnet test` -let runTestsWithNetFX = - BuildTask.create "RunTestsNetFX" [ clean; build; buildTests; buildTestsNetFX] { - testProjects - @ testProjectsNetFX + +let createRunTestTask (name: string) (deps: BuildTask.TaskInfo list) (projects: ProjectInfo list) = + BuildTask.create name deps { + projects |> Seq.iter (fun testProjectInfo -> Fake.DotNet.DotNet.test (fun testParams -> @@ -61,27 +44,20 @@ let runTestsWithNetFX = testProjectInfo.ProjFile) } -// to do: use this once we have actual tests -let runTestsWithCodeCov = - BuildTask.create "RunTestsWithCodeCov" [ clean; build ] { - let standardParams = - Fake.DotNet.MSBuild.CliArguments.Create() +/// runs the all test projects via `dotnet test` +let runTestsAll = createRunTestTask "RunTestsAll" [ clean; build; buildTestsAll ] (testProjectsCore @ testProjectsExtensionsLibs @ testProjectsNetFX) - testProjects - |> Seq.iter (fun testProjectInfo -> - Fake.DotNet.DotNet.test - (fun testParams -> - { testParams with - MSBuildParams = - { standardParams with - Properties = - [ - "AltCover", "true" - "AltCoverCobertura", "../../codeCov.xml" - "AltCoverForce", "true" - ] - } - Logger = Some "console;verbosity=detailed" - }) - testProjectInfo.ProjFile) - } +/// runs the all test projects except those targeting netfx via `dotnet test` +let runTestsAllNoNetFX = createRunTestTask "RunTestsAllNoNetFX" [ clean; build; buildTestsAllNoNetFX ] (testProjectsCore @ testProjectsExtensionsLibs) + +/// runs the core test projects via `dotnet test` +let runTestsCore = createRunTestTask "RunTestsCore" [ clean; build; buildTestsCore; buildTestsCore] testProjectsCore + +/// runs the core netfx test projects via `dotnet test` +let runTestsNetFX = createRunTestTask "RunTestsNetFX" [ clean; build; buildTestsNetFX;] testProjectsNetFX + +/// runs the core and core netfx test projects via `dotnet test` +let runTestsCoreWithNetFX = createRunTestTask "RunTestsCoreWithNetFX" [ clean; build; buildTestsCore; buildTestsNetFX;] (testProjectsCore @ testProjectsNetFX) + +/// runs the extension lib test projects via `dotnet test` +let runTestsExtensionLibs = createRunTestTask "RunTestsExtensionLibs" [ clean; build; buildTestsExtensionsLibs] testProjectsExtensionsLibs \ No newline at end of file diff --git a/docs/img/notebook_quickstart_csharp.png b/docs/img/notebook_quickstart_csharp.png new file mode 100644 index 000000000..9b293f8c2 Binary files /dev/null and b/docs/img/notebook_quickstart_csharp.png differ diff --git a/docs/img/notebook_quickstart_fsharp.png b/docs/img/notebook_quickstart_fsharp.png new file mode 100644 index 000000000..4387db8ac Binary files /dev/null and b/docs/img/notebook_quickstart_fsharp.png differ diff --git a/docs/img/showcase.gif b/docs/img/showcase.gif new file mode 100644 index 000000000..e51eaa7be Binary files /dev/null and b/docs/img/showcase.gif differ diff --git a/docs/img/showcase.png b/docs/img/showcase.png new file mode 100644 index 000000000..576894f78 Binary files /dev/null and b/docs/img/showcase.png differ diff --git a/tests/Common/CSharpTestBase/CSharpTestBase.csproj b/tests/Common/CSharpTestBase/CSharpTestBase.csproj new file mode 100644 index 000000000..132c02c59 --- /dev/null +++ b/tests/Common/CSharpTestBase/CSharpTestBase.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/tests/Common/CSharpTestBase/Class1.cs b/tests/Common/CSharpTestBase/Class1.cs new file mode 100644 index 000000000..96d64f58e --- /dev/null +++ b/tests/Common/CSharpTestBase/Class1.cs @@ -0,0 +1,5 @@ +namespace CSharpTestBase; +public class Class1 +{ + +} diff --git a/tests/Common/FSharpTestBase/FSharpTestBase.fsproj b/tests/Common/FSharpTestBase/FSharpTestBase.fsproj new file mode 100644 index 000000000..8a17f4544 --- /dev/null +++ b/tests/Common/FSharpTestBase/FSharpTestBase.fsproj @@ -0,0 +1,36 @@ + + + + net6.0 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Common/FSharpTestBase/TestCharts/Chart2DTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/Chart2DTestCharts.fs new file mode 100644 index 000000000..763c6dbd5 --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/Chart2DTestCharts.fs @@ -0,0 +1,480 @@ +module Chart2DTestCharts + +open System +open Plotly.NET +open Plotly.NET.TraceObjects + +open TestUtils.DataGeneration + +module Point = + + let ``Point chart with full plotly.js reference`` = + let xData = [0. .. 10.] + let yData = [0. .. 10.] + Chart.Point(xData, yData, UseDefaults = false) + |> Chart.withDisplayOptions( + DisplayOptions.init( + PlotlyJSReference = Full + ) + ) + + let ``Point chart with plotly.js cdn reference`` = + let xData = [0. .. 10.] + let yData = [0. .. 10.] + Chart.Point(xData, yData, UseDefaults = false) + |> Chart.withDisplayOptions( + DisplayOptions.init( + PlotlyJSReference = CDN $"https://cdn.plot.ly/plotly-2.0.0.min" + ) + ) + + let ``Point chart referencing plotly.js using require.js`` = + let xData = [0. .. 10.] + let yData = [0. .. 10.] + Chart.Point(xData, yData, UseDefaults = false) + |> Chart.withDisplayOptions( + DisplayOptions.init( + PlotlyJSReference = Require $"https://cdn.plot.ly/plotly-{Globals.PLOTLYJS_VERSION}.min" + ) + ) + + let ``Point chart with axis labels and title`` = + let xData = [0. .. 10.] + let yData = [0. .. 10.] + Chart.Point(x = xData, y = yData, UseDefaults = false) + |> Chart.withTitle "Hello world!" + |> Chart.withXAxisStyle (TitleText = "xAxis", ShowGrid=false) + |> Chart.withYAxisStyle (TitleText = "yAxis", ShowGrid=false) + + let ``Point chart with text labels`` = + let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + let labels = ["a";"b";"c";"d";"e";"f";"g";"h";"i";"j";] + Chart.Point( + x = x,y = y, + Name="points", + MultiText=labels, + TextPosition=StyleParam.TextPosition.TopRight, + UseDefaults = false + ) + +module Line = + + let ``Simple line chart`` = Chart.Line([ for x in 1.0 .. 100.0 -> (x, x ** 2.0) ], UseDefaults = false) + + let ``Line chart with line styling`` = + let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + Chart.Line( + x = x, + y = y, + Name="line", + ShowMarkers=true, + MarkerSymbol=StyleParam.MarkerSymbol.Square, + UseDefaults = false + ) + |> Chart.withLineStyle(Width=2.,Dash=StyleParam.DrawingStyle.Dot) + +module Spline = + + let ``Simple spline chart`` = + let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + Chart.Spline(x = x, y = y, Name="spline", UseDefaults = false) + +module Bubble = + + let ``Simple bubble chart`` = + let x = [2; 4; 6;] + let y = [4; 1; 6;] + let size = [19; 26; 55;] + Chart.Bubble(x = x, y = y,sizes = size, UseDefaults = false) + +module Range = + + let ``Styled range chart`` = + let rnd = System.Random(5) + + let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + + let yUpper = y |> List.map (fun v -> v + rnd.NextDouble()) + let yLower = y |> List.map (fun v -> v - rnd.NextDouble()) + Chart.Range( + x = x, + y = y, + upper = yUpper, + lower = yLower, + mode = StyleParam.Mode.Lines_Markers, + MarkerColor=Color.fromString "grey", + RangeColor=Color.fromString "lightblue", + UseDefaults = false) + +module Area = + + let ``Simple area chart`` = + let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] + let y = [5.; 2.5; 5.; 7.5; 5.; 2.5; 7.5; 4.5; 5.5; 5.] + Chart.Area(x = x, y = y, UseDefaults = false) + +module SplineArea = + + let ``Simple spline area chart`` = + let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] + let y = [5.; 2.5; 5.; 7.5; 5.; 2.5; 7.5; 4.5; 5.5; 5.] + Chart.SplineArea(x = x, y = y, UseDefaults = false) + +module StackedArea = + + let ``Two stacked areas chart`` = + let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] + let y = [5.; 2.5; 5.; 7.5; 5.; 2.5; 7.5; 4.5; 5.5; 5.] + [ + Chart.StackedArea(x = x, y = y, UseDefaults = false) + Chart.StackedArea(x = x, y = (y |> Seq.rev), UseDefaults = false) + ] + |> Chart.combine + +module Funnel = + + let ``Simple funnel chart`` = + let y = [|"Sales person A"; "Sales person B"; "Sales person C"; "Sales person D"; "Sales person E"|] + let x = [|1200.; 909.4; 600.6; 300.; 80.|] + + // Customize the connector lines used to connect the funnel bars + let connectorLine = Line.init (Color=Color.fromString "royalblue", Dash=StyleParam.DrawingStyle.Dot, Width=3.) + let connector = FunnelConnector.init(Line=connectorLine) + + // Customize the outline of the funnel bars + let line = Line.init(Width=2.,Color=Color.fromString "3E4E88") + + Chart.Funnel (x = x, y = y,MarkerColor=Color.fromString "59D4E8", MarkerOutline=line, Connector=connector, UseDefaults = false) + |> Chart.withMarginSize(Left=100) + + +module StackedFunnel = () + +module Waterfall = () + +module Bar = + + let ``Simple bar chart`` = + let values = [20; 14; 23;] + let keys = ["Product A"; "Product B"; "Product C";] + Chart.Bar(values = values, Keys = keys, UseDefaults = false) + + let ``Two stacked bars chart`` = + let values = [20; 14; 23;] + let keys = ["Product A"; "Product B"; "Product C";] + [ + Chart.StackedBar(values = values, Keys = keys, Name="old", UseDefaults = false); + Chart.StackedBar(values = [8; 21; 13;], Keys = keys, Name="new", UseDefaults = false) + ] + |> Chart.combine + +module StackedBar = () + +module Column = + + let ``Simple column chart`` = + let values = [20; 14; 23;] + let keys = ["Product A"; "Product B"; "Product C";] + Chart.Column(values = values, Keys = keys, UseDefaults = false) + + let ``Two stacked columns chart`` = + let values = [20; 14; 23;] + let keys = ["Product A"; "Product B"; "Product C";] + [ + Chart.StackedColumn(values = values, Keys = keys,Name="old", UseDefaults = false); + Chart.StackedColumn(values = [8; 21; 13;], Keys = keys,Name="new", UseDefaults = false) + ] + |> Chart.combine + +module StackedColumn = () + +module Histogram = + + let ``Simple histogram`` = + let rnd = System.Random(5) + let x = [for i=0 to 500 do yield rnd.NextDouble() ] + Chart.Histogram(X = x, UseDefaults = false) + |> Chart.withSize(500, 500) + + +module Histogram2D = + + let ``Histogram2D of 2D normal distribution`` = + let rnd = System.Random(5) + let n = 2000 + let a = -1. + let b = 1.2 + let step i = a + ((b - a) / float (n - 1)) * float i + + //---------------------- generate data distributed in x and y direction ---------------------- + let x = Array.init n (fun i -> ((step i)**3.) + (0.3 * (normal (rnd) 0. 2.) )) + let y = Array.init n (fun i -> ((step i)**6.) + (0.3 * (normal (rnd) 0. 2.) )) + Chart.Histogram2D (x = x, y = y, UseDefaults = false) + +module BoxPlot = + + let ``Simple boxplot with boxpoints`` = + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] + Chart.BoxPlot(X = x, Y = y,Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All, UseDefaults = false) + + let ``Simple horizontal boxplot with boxpoints`` = + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] + Chart.BoxPlot(X = y,Y = x,Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All,Orientation=StyleParam.Orientation.Horizontal, UseDefaults = false) + + let ``Combined chart with 2 horizontal boxplots`` = + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + let y' = [2.; 1.5; 5.; 1.5; 2.; 2.5; 2.1; 2.5; 1.5; 1.;2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + [ + Chart.BoxPlot(data = y , orientation = StyleParam.Orientation.Horizontal, Name="bin1",Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All, UseDefaults = false); + Chart.BoxPlot(data = y', orientation = StyleParam.Orientation.Horizontal, Name="bin2",Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All, UseDefaults = false); + ] + |> Chart.combine + +module Violin = + + let ``Simple violin plot with jitterpoints`` = + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] + Chart.Violin ( + X = x, Y = y, + Points=StyleParam.JitterPoints.All, + UseDefaults = false + ) + + let ``Simple horizontal violin plot with jitterpoints`` = + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] + Chart.Violin( + X = y, Y = x, + Jitter=0.1, + Points=StyleParam.JitterPoints.All, + Orientation=StyleParam.Orientation.Horizontal, + MeanLine=MeanLine.init(Visible=true), + UseDefaults = false + ) + + let ``Combined chart with 2 horizontal violin plots`` = + let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + let y' = [2.; 1.5; 5.; 1.5; 2.; 2.5; 2.1; 2.5; 1.5; 1.;2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] + [ + Chart.Violin (data = y , orientation = StyleParam.Orientation.Horizontal, Name="bin1",Jitter=0.1,Points=StyleParam.BoxPoints.All, UseDefaults = false) + Chart.Violin (data = y', orientation = StyleParam.Orientation.Horizontal, Name="bin2",Jitter=0.1,Points=StyleParam.BoxPoints.All, UseDefaults = false) + ] + |> Chart.combine + +module Histogram2DContour = + + let ``Histogram2DContour of 2D normal distribution`` = + + let rnd = System.Random(5) + let n = 2000 + let a = -1. + let b = 1.2 + let step i = a + ((b - a) / float (n - 1)) * float i + + //---------------------- generate data distributed in x and y direction ---------------------- + let x = Array.init n (fun i -> ((step i)**3.) + (0.3 * (normal (rnd) 0. 2.) )) + let y = Array.init n (fun i -> ((step i)**6.) + (0.3 * (normal (rnd) 0. 2.) )) + [ + Chart.Histogram2DContour (x = x, y = y,ContourLine=Line.init(Width=0.), UseDefaults = false) + Chart.Point(x = x,y = y,Opacity=0.3, UseDefaults = false) + ] + |> Chart.combine + +module Heatmap = + + let ``simple heatmap with custom colorscale`` = + let matrix = + [[1.;1.5;0.7;2.7]; + [2.;0.5;1.2;1.4]; + [0.1;2.6;2.4;3.0];] + + let rownames = ["p3";"p2";"p1"] + let colnames = ["Tp0";"Tp30";"Tp60";"Tp160"] + + let colorscaleValue = + StyleParam.Colorscale.Custom [(0.0,Color.fromString "#3D9970");(1.0,Color.fromString "#001f3f")] + + Chart.Heatmap( + zData = matrix, + colNames = colnames, + rowNames = rownames, + ColorScale=colorscaleValue, + ShowScale=true, + UseDefaults = false + ) + |> Chart.withSize(700,500) + |> Chart.withMarginSize(Left=200.) + + + let ``Simple heatmap with custom colorscale and styled colorbar`` = + let matrix = + [[1.;1.5;0.7;2.7]; + [2.;0.5;1.2;1.4]; + [0.1;2.6;2.4;3.0];] + + let rownames = ["p3";"p2";"p1"] + let colnames = ["Tp0";"Tp30";"Tp60";"Tp160"] + + let colorscaleValue = + StyleParam.Colorscale.Custom [(0.0,Color.fromString "#3D9970");(1.0,Color.fromString "#001f3f")] + + Chart.Heatmap( + zData = matrix, + colNames = colnames, + rowNames = rownames, + ColorScale=colorscaleValue, + ShowScale=true, + UseDefaults = false + ) + |> Chart.withSize(700.,500.) + |> Chart.withMarginSize(Left=200.) + |> Chart.withColorBarStyle(TitleText = "Im the Colorbar") + + +module AnnotatedHeatmap = + + + let ``Simple annotated heatmap`` = + Chart.AnnotatedHeatmap( + zData = [ + [1..5] + [6..10] + [11..15] + ], + annotationText = [ + ["1,1";"1,2";"1,3"] + ["2,1";"2,2";"2,3"] + ["3,1";"3,2";"3,3"] + ], + X = ["C1";"C2";"C3"], + Y = ["R1";"R2";"R3"], + ReverseYAxis = true, + UseDefaults = false + ) + +module Image = + + let private colors = [ + [[0 ;0 ;255]; [255;255;0 ]; [0 ;0 ;255]] + [[255;0 ;0 ]; [255;0 ;255]; [255;0 ;255]] + [[0 ;255;0 ]; [0 ;255;255]; [255;0 ;0 ]] + ] + + let ``Raw color component image chart`` = + Chart.Image(Z=colors, UseDefaults = false) + |> Chart.withTitle "Image chart from raw color component arrays" + + let ``HSL image chart`` = + Chart.Image(Z=colors, ColorModel=StyleParam.ColorModel.HSL, UseDefaults = false) + |> Chart.withTitle "HSL color model" + + let ``ARGB image chart`` = + let argbs = [ + [ColorKeyword.AliceBlue ; ColorKeyword.CornSilk ; ColorKeyword.LavenderBlush ] |> List.map ARGB.fromKeyword + [ColorKeyword.DarkGray ; ColorKeyword.Snow ; ColorKeyword.MidnightBlue ] |> List.map ARGB.fromKeyword + [ColorKeyword.LightSteelBlue; ColorKeyword.DarkKhaki; ColorKeyword.LightAkyBlue ] |> List.map ARGB.fromKeyword + ] + Chart.Image(z = argbs, UseDefaults = false) + |> Chart.withTitle "ARGB image chart" + + let ``Image chart from base64 string`` = + let base64String = TestUtils.HtmlCodegen.getLogoPNG() + Chart.Image( + Source=($"data:image/jpg;base64,{base64String}"), + UseDefaults = false + ) + |> Chart.withTitle "This is Plotly.NET:" + +module Contour = + + let ``Contour plot with peak and sink`` = + + // Create example data + let size = 100 + let x = linspace(-2. * Math.PI, 2. * Math.PI, size) + let y = linspace(-2. * Math.PI, 2. * Math.PI, size) + + let f x y = - (5. * x / (x**2. + y**2. + 1.) ) + + let z = + Array.init size (fun i -> + Array.init size (fun j -> + f x.[j] y.[i] + ) + ) + + Chart.Contour(zData = z, UseDefaults = false) + |> Chart.withSize(600.,600.) + +module OHLC = + + let internal candles = + [|("2020-01-17T13:40:00", 0.68888, 0.68888, 0.68879, 0.6888); + ("2020-01-17T13:41:00", 0.68883, 0.68884, 0.68875, 0.68877); + ("2020-01-17T13:42:00", 0.68878, 0.68889, 0.68878, 0.68886); + ("2020-01-17T13:43:00", 0.68886, 0.68886, 0.68876, 0.68879); + ("2020-01-17T13:44:00", 0.68879, 0.68879, 0.68873, 0.68874); + ("2020-01-17T13:45:00", 0.68875, 0.68877, 0.68867, 0.68868); + ("2020-01-17T13:46:00", 0.68869, 0.68887, 0.68869, 0.68883); + ("2020-01-17T13:47:00", 0.68883, 0.68899, 0.68883, 0.68899); + ("2020-01-17T13:48:00", 0.68898, 0.689, 0.68885, 0.68889); + ("2020-01-17T13:49:00", 0.68889, 0.68893, 0.68881, 0.68893); + ("2020-01-17T13:50:00", 0.68891, 0.68896, 0.68886, 0.68891); + |] + |> Array.map (fun (d,o,h,l,c)->System.DateTime.Parse d, StockData.Create(o,h,l,c)) + + let ``Simple OHLC chart`` = Chart.OHLC(stockTimeSeries = candles, UseDefaults = false) + + let ``Simple OHLC chart without range slider`` = Chart.OHLC(stockTimeSeries = candles, ShowXAxisRangeSlider = false, UseDefaults = false) + +module Candlestick = + + let ``Simple candlestick chart`` = Chart.Candlestick(stockTimeSeries = OHLC.candles, UseDefaults = false) + + let ``Simple candlestick chart without range slider`` = Chart.Candlestick(stockTimeSeries = OHLC.candles, ShowXAxisRangeSlider = false, UseDefaults = false) + + +module Splom = + + let ``Simple scatterplot matrix`` = + let data = + [ + "A",[|1.;4.;3.4;0.7;|] + "B",[|3.;1.5;1.7;2.3;|] + "C",[|2.;4.;3.1;5.|] + "D",[|4.;2.;2.;4.;|] + ] + Chart.Splom(keyValues = data, MarkerColor=Color.fromString "blue", UseDefaults = false) + + +module PointDensity = + + + let ``Simple PointDensity chart`` = + let rnd = new System.Random(5) + let x = [for i in 0 .. 100 -> rnd.NextDouble()] + let y = [for i in 0 .. 100 -> rnd.NextDouble()] + Chart.PointDensity(x = x,y = y,UseDefaults = false) + + let ``Styled PointDensity chart`` = + let rnd = new System.Random(5) + let x = [for i in 0 .. 100 -> rnd.NextDouble()] + let y = [for i in 0 .. 100 -> rnd.NextDouble()] + Chart.PointDensity( + x = x, + y = y, + PointMarkerColor = Color.fromKeyword Purple, + PointMarkerSymbol = StyleParam.MarkerSymbol.X, + PointMarkerSize = 4, + ColorScale = StyleParam.Colorscale.Viridis, + ColorBar = ColorBar.init(Title = Title.init("Density")), + ShowContourLabels = true, + UseDefaults = false + ) diff --git a/tests/Common/FSharpTestBase/TestCharts/Chart3DTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/Chart3DTestCharts.fs new file mode 100644 index 000000000..e61be4608 --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/Chart3DTestCharts.fs @@ -0,0 +1,174 @@ +module Chart3DTestCharts + +open Plotly.NET +open Plotly.NET.TraceObjects +open System + +open TestUtils.DataGeneration + +module Scatter3D = + + let ``Simple scatter3d chart with axis titles`` = + let x = [19; 26; 55;] + let y = [19; 26; 55;] + let z = [19; 26; 55;] + + Chart.Scatter3D(x = x,y = y,z = z, mode = StyleParam.Mode.Markers, UseDefaults = false) + |> Chart.withXAxisStyle("my x-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withYAxisStyle("my y-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withZAxisStyle("my z-axis") + |> Chart.withSize(800,800) + +module Point3D = + + let ``Simple Point3D chart with axis titles`` = + let x = [19; 26; 55;] + let y = [19; 26; 55;] + let z = [19; 26; 55;] + + Chart.Point3D(x = x,y = y,z = z, UseDefaults = false) + |> Chart.withXAxisStyle("my x-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withYAxisStyle("my y-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withZAxisStyle("my z-axis") + |> Chart.withSize(800,800) + + + +module Line3D = + let ``Upwards spiral line 3D chart with markers`` = + let c = [0. .. 0.5 .. 15.] + + let x, y, z = + c + |> List.map (fun i -> + let i' = float i + let r = 10. * Math.Cos (i' / 10.) + (r * Math.Cos i', r * Math.Sin i', i') + |> fun (x,y,z) -> + Math.Round(x,3), + Math.Round(y,3), + Math.Round(z,3) + ) + |> List.unzip3 + + Chart.Line3D(x = x, y = y, z = z, ShowMarkers=true, UseDefaults = false) + |> Chart.withXAxisStyle("x-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withYAxisStyle("y-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withZAxisStyle("z-axis") + |> Chart.withSize(800, 800) + + +module Bubble3D = + + let ``Simple Bubble3D chart with axis titles`` = + Chart.Bubble3D( + xyz = [1,3,2; 6,5,4; 7,9,8], + sizes = [20; 40; 30], + MultiText = ["A"; "B"; "C"], + TextPosition = StyleParam.TextPosition.TopLeft, + UseDefaults = false + ) + |> Chart.withXAxisStyle("x-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withYAxisStyle("y-axis", Id=StyleParam.SubPlotId.Scene 1) + |> Chart.withZAxisStyle("z-axis") + +module Surface = + + let ``Peak and sink surface plot`` = + + //---------------------- Create example data ---------------------- + let size = 100 + let x = linspace(-2. * Math.PI, 2. * Math.PI, size) + let y = linspace(-2. * Math.PI, 2. * Math.PI, size) + + let f x y = - (5. * x / (x**2. + y**2. + 1.) ) + + let z = + Array.init size (fun i -> + Array.init size (fun j -> f x.[j] y.[i] ) + ) + + Chart.Surface(zData = z, UseDefaults = false) + + let ``Surface plot with x/y indices mapping to z matrix with contours`` = + let x' = [0.;2.5] + let y' = [0.;2.5] + let z' = [ + [1.;1.;]; // row wise (length x) + [1.;2.;]; + ] // column (length y) + + Chart.Surface(zData = z', X = x', Y = y', Opacity=0.5, Contours=Contours.initXyz(Show=true), UseDefaults = false) + + +module Mesh3D = + + let ``Mesh3D chart with random x/x/z data`` = + + let rnd = System.Random(5) + let x = Array.init 50 (fun _ -> rnd.NextDouble()) + let y = Array.init 50 (fun _ -> rnd.NextDouble()) + let z = Array.init 50 (fun _ -> rnd.NextDouble()) + + Chart.Mesh3D( + x = x, + y = y, + z = z, + FlatShading = true, + Contour = Contour.init(Show = true), + UseDefaults = false + ) + +module Cone = + + let ``Simple cone chart`` = + Chart.Cone( + x = [1; 1; 1], + y = [1; 2; 3], + z = [1; 1; 1], + u = [1; 2; 3], + v = [1; 1; 2], + w = [4; 4; 1], + ColorScale = StyleParam.Colorscale.Viridis, + UseDefaults = false + ) + +module StreamTube = + + let ``Simple StreamTube chart `` = + Chart.StreamTube( + x = [0; 0; 0], + y = [0; 1; 2], + z = [0; 0; 0], + u = [0; 0; 0], + v = [1; 1; 1], + w = [0; 0; 0], + ColorScale = StyleParam.Colorscale.Viridis, + UseDefaults = false + ) + +module Volume = + + let ``Fancy mgrid based volume chart`` = + let x,y,z = mgrid(1.,2.,4) + Chart.Volume( + x = (x |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + y = (y |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + z = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + value = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + ColorScale = StyleParam.Colorscale.Viridis, + UseDefaults = false + ) + +module IsoSurface = + + let ``Fancy mgrid based isosurface chart`` = + let x,y,z = mgrid(1.,2.,4) + Chart.IsoSurface( + x = (x |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + y = (y |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + z = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + value = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), + ColorScale = StyleParam.Colorscale.Viridis, + UseDefaults = false + ) \ No newline at end of file diff --git a/tests/Common/FSharpTestBase/TestCharts/ChartCarpetTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/ChartCarpetTestCharts.fs new file mode 100644 index 000000000..b185aad25 --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/ChartCarpetTestCharts.fs @@ -0,0 +1,110 @@ +module ChartCarpetTestCharts + + +open Plotly.NET +open Plotly.NET.TraceObjects +open Plotly.NET.LayoutObjects +open System + +open TestUtils.DataGeneration +open Deedle + +let internal a = [4.; 4.; 4.; 4.5; 4.5; 4.5; 5.; 5.; 5.; 6.; 6.; 6.] +let internal b = [1.; 2.; 3.; 1.; 2.; 3.; 1.; 2.; 3.; 1.; 2.; 3.] +let internal y = [2.; 3.5; 4.; 3.; 4.5; 5.; 5.5; 6.5; 7.5; 8.; 8.5; 10.] + +let internal carpets = + [ + Chart.Carpet(carpetId = "carpet1",A = a, B = b, Y = y, UseDefaults = false) + Chart.Carpet(carpetId = "carpet2",A = (a |> List.rev) , B = (b |> List.rev), Y = (y |> List.map (fun x -> x + 10.)), UseDefaults = false) + Chart.Carpet(carpetId = "carpet3",A = a, B = b, Y = (y |> List.map (fun x -> x + 20.)), UseDefaults = false) + Chart.Carpet(carpetId = "carpet4",A = (a |> List.rev) , B = (b |> List.rev), Y = (y |> List.map (fun x -> x + 30.)), UseDefaults = false) + Chart.Carpet(carpetId = "carpet5",A = a, B = b, Y = (y |> List.map (fun x -> x + 40.)), UseDefaults = false) + ] + +let internal aData = [4.; 5.; 5.; 6.] +let internal bData = [1.; 1.; 2.; 3.] +let internal sizes = [5; 10; 15; 20] + +let internal carpetCharts = + [ + Chart.ScatterCarpet( + a = aData,b = bData, + mode = StyleParam.Mode.Lines_Markers, + carpetAnchorId = "carpet1", + Name = "Scatter", + MultiMarkerSymbol =[ + StyleParam.MarkerSymbol.ArrowDown + StyleParam.MarkerSymbol.TriangleNW + StyleParam.MarkerSymbol.DiamondX + StyleParam.MarkerSymbol.Hexagon2 + ], + MarkerColor = Color.fromColors ([Red; Blue; Green; Yellow] |> List.map Color.fromKeyword), + UseDefaults = false + ) + Chart.PointCarpet(a = aData, b = bData, carpetAnchorId = "carpet2",Name = "Point", UseDefaults = false) + Chart.LineCarpet(a = aData, b = bData, carpetAnchorId = "carpet3",Name = "Line", UseDefaults = false) + Chart.SplineCarpet(a = aData, b = bData, carpetAnchorId = "carpet4",Name = "Spline", UseDefaults = false) + Chart.BubbleCarpet(absizes = (Seq.zip3 aData bData sizes), carpetAnchorId = "carpet5",Name = "Bubble", UseDefaults = false) + ] + +module Carpet = + + let ``Base carpet chart`` = carpets[0] + +module ScatterCarpet = + + let ``Simple carpet scatter chart`` = Chart.combine [carpets.[0]; carpetCharts.[0]] + +module PointCarpet = + + let ``Simple carpet point chart`` = Chart.combine [carpets.[1]; carpetCharts.[1]] + +module LineCarpet = + + let ``Simple carpet line chart`` = Chart.combine [carpets.[2]; carpetCharts.[2]] + +module SplineCarpet = + + let ``Simple carpet spline chart`` = Chart.combine [carpets.[3]; carpetCharts.[3]] + +module BubbleCarpet = + + let ``Simple carpet bubble chart`` = Chart.combine [carpets.[4]; carpetCharts.[4]] + +module ContourCarpet = + + let ``Styled carpet contour chart`` = + [ + Chart.Carpet( + carpetId = "contour", + A = [0.; 1.; 2.; 3.; 0.; 1.; 2.; 3.; 0.; 1.; 2.; 3.], + B = [4.; 4.; 4.; 4.; 5.; 5.; 5.; 5.; 6.; 6.; 6.; 6.], + X = [2.; 3.; 4.; 5.; 2.2; 3.1; 4.1; 5.1; 1.5; 2.5; 3.5; 4.5], + Y = [1.; 1.4; 1.6; 1.75; 2.; 2.5; 2.7; 2.75; 3.; 3.5; 3.7; 3.75], + AAxis = LinearAxis.initCarpet( + TickPrefix = "a = ", + Smoothing = 0., + MinorGridCount = 9, + AxisType = StyleParam.AxisType.Linear + ), + BAxis = LinearAxis.initCarpet( + TickPrefix = "b = ", + Smoothing = 0., + MinorGridCount = 9, + AxisType = StyleParam.AxisType.Linear + ), + UseDefaults = false, + Opacity = 0.75 + ) + Chart.ContourCarpet( + z = [1.; 1.96; 2.56; 3.0625; 4.; 5.0625; 1.; 7.5625; 9.; 12.25; 15.21; 14.0625], + carpetAnchorId = "contour", + A = [0; 1; 2; 3; 0; 1; 2; 3; 0; 1; 2; 3], + B = [4; 4; 4; 4; 5; 5; 5; 5; 6; 6; 6; 6], + UseDefaults = false, + ContourLineColor = Color.fromKeyword White, + ShowContourLabels = true + ) + ] + |> Chart.combine \ No newline at end of file diff --git a/tests/Common/FSharpTestBase/TestCharts/ChartDomainTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/ChartDomainTestCharts.fs new file mode 100644 index 000000000..4cc3716fe --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/ChartDomainTestCharts.fs @@ -0,0 +1,481 @@ +module ChartDomainTestCharts + +open Plotly.NET +open Plotly.NET.TraceObjects + +module Pie = + + let ``Simple pie chart`` = + let values = [19; 26; 55;] + let labels = ["Residential"; "Non-Residential"; "Utility"] + Chart.Pie(values = values, Labels = labels, UseDefaults = false) + + let ``Styled pie chart`` = + + let values = [19; 26; 55;] + let labels = ["Residential"; "Non-Residential"; "Utility"] + + Chart.Pie( + values = values, + Labels = labels, + SectionColors = [ + Color.fromKeyword Aqua + Color.fromKeyword Salmon + Color.fromKeyword Tan + ], + SectionOutlineColor = Color.fromKeyword Black, + SectionOutlineWidth = 2., + MultiText = [ + "Some" + "More" + "Stuff" + ], + MultiTextPosition = [ + StyleParam.TextPosition.Inside + StyleParam.TextPosition.Outside + StyleParam.TextPosition.Inside + ], + Rotation = 45., + MultiPull = [0.; 0.3; 0.], + UseDefaults = false + ) + +module Doughnut = + + let ``Simple doughnut chart`` = + let values = [19; 26; 55;] + let labels = ["Residential"; "Non-Residential"; "Utility"] + Chart.Doughnut( + values = values, + Labels = labels, + Hole=0.3, + MultiText=labels, + UseDefaults = false + ) + +module FunnelArea = + + let ``Simple funnelarea chart`` = + let values = [|5; 4; 3; 2; 1|] + let text = [|"The 1st"; "The 2nd"; "The 3rd"; "The 4th"; "The 5th"|] + let line = Line.init (Color=Color.fromString "purple", Width=3.) + Chart.FunnelArea(values=values, MultiText=text, SectionOutline=line, UseDefaults = false) + + + + let ``Styled funnelarea chart`` = + let values = [|5; 4; 3|] + let labels = [|"The 1st"; "The 2nd"; "The 3rd"|] + + Chart.FunnelArea( + values = values, + Labels = labels, + MultiText = labels, + SectionColors = [ + Color.fromKeyword Aqua + Color.fromKeyword Salmon + Color.fromKeyword Tan + ], + SectionOutlineColor = Color.fromKeyword Black, + SectionOutlineWidth = 2., + AspectRatio = 0.75, + BaseRatio = 0.1, + UseDefaults = false + ) + +module Sunburst = + + + let ``Simple sunburst chart`` = + Chart.Sunburst( + labels = ["A";"B";"C";"D";"E"], + parents = ["";"";"B";"B";""], + Values=[5.;0.;3.;2.;3.], + MultiText=["At";"Bt";"Ct";"Dt";"Et"], + UseDefaults = false + ) + + let ``Styled sunburst chart`` = + let labelsParents = [ + ("A",""), 20 + ("B",""), 1 + ("C",""), 2 + ("D",""), 3 + ("E",""), 4 + + ("AA","A"), 15 + ("AB","A"), 5 + + ("BA","B"), 1 + + ("AAA", "AA"), 10 + ("AAB", "AA"), 5 + ] + + Chart.Sunburst( + labelsparents = (labelsParents |> Seq.map fst), + Values = (labelsParents |> Seq.map snd), + BranchValues = StyleParam.BranchValues.Total, // branch values are the total of their childrens values + SectionColorScale = StyleParam.Colorscale.Viridis, + ShowSectionColorScale = true, + SectionOutlineColor = Color.fromKeyword Black, + Rotation = 45, + UseDefaults = false + ) + +module Treemap = + + + let ``Styled treemap chart`` = + let labelsParents = [ + ("A",""), 20 + ("B",""), 1 + ("C",""), 2 + ("D",""), 3 + ("E",""), 4 + + ("AA","A"), 15 + ("AB","A"), 5 + + ("BA","B"), 1 + + ("AAA", "AA"), 10 + ("AAB", "AA"), 5 + ] + + Chart.Treemap( + labelsparents = (labelsParents |> Seq.map fst), + Values = (labelsParents |> Seq.map snd), + BranchValues = StyleParam.BranchValues.Total, // branch values are the total of their childrens values + SectionColorScale = StyleParam.Colorscale.Viridis, + ShowSectionColorScale = true, + SectionOutlineColor = Color.fromKeyword Black, + Tiling = TreemapTiling.init(Packing = StyleParam.TreemapTilingPacking.SliceDice), + UseDefaults = false + ) + +module ParallelCoord = + + let ``Simple parallel coordinates chart`` = + let data = + [ + "A",[1.;4.;3.4;0.7;] + "B",[3.;1.5;1.7;2.3;] + "C",[2.;4.;3.1;5.] + "D",[4.;2.;2.;4.;] + ] + Chart.ParallelCoord(keyValues = data,LineColor=Color.fromString "blue", UseDefaults = false) + + let ``Styled parallel coordinates chart`` = + + let dims = + [ + Dimension.initParallel(Label = "1", Values = ([1;2;3;4] ), Range = StyleParam.Range.MinMax(0., 8.)) + Dimension.initParallel(Label = "2", Values = ([1;2;3;4] |> List.rev ), Range = StyleParam.Range.MinMax(0., 8.)) + Dimension.initParallel(Label = "3", Values = ([1;2;3;4] ), Range = StyleParam.Range.MinMax(0., 8.)) + Dimension.initParallel(Label = "4", Values = ([1;2;3;4] |> List.rev ), Range = StyleParam.Range.MinMax(0., 8.)) + ] + + let colors = + [1;2;3;4] + |> Color.fromColorScaleValues + + Chart.ParallelCoord( + dimensions = dims, + LineColorScale = StyleParam.Colorscale.Viridis, + LineColor = colors, + UseDefaults = false + ) + +module ParallelCategories = + + let ``Simple parallel categories chart`` = + let dims = + [ + Dimension.initParallel(Values = ["Cat1";"Cat1";"Cat1";"Cat1";"Cat2";"Cat2";"Cat3"],Label="A") + Dimension.initParallel(Values = [0;1;0;1;0;0;0],Label="B",TickText=["YES";"NO"]) + ] + + Chart.ParallelCategories( + dimensions = dims, + LineColor=Color.fromColorScaleValues [0.;1.;0.;1.;0.;0.;0.], + LineColorScale = StyleParam.Colorscale.Blackbody, + UseDefaults = false + ) + + let ``Styled parallel categories chart`` = + let dims = + [ + Dimension.initParallel(Values = ["A";"A";"A";"B";"B";"B";"C";"D"],Label="Lvl1") + Dimension.initParallel(Values = ["AA";"AA";"AB";"AB";"AB";"AB";"AB";"AB"],Label="Lvl2") + Dimension.initParallel(Values = ["AAA";"AAB";"AAC";"AAC";"AAB";"AAB";"AAA";"AAA"],Label="Lvl3") + ] + + Chart.ParallelCategories( + dimensions = dims, + LineColor = Color.fromColorScaleValues [0; 1; 2; 2; 1; 1; 0; 0], // These values map to the last category axis, meaning [AAA => 0; AAB = 1; AAC => 2] + LineColorScale = StyleParam.Colorscale.Viridis, + BundleColors = false, + UseDefaults = false + ) + +module Sankey = + + let ``Styled sankey chart`` = + Chart.Sankey( + nodeLabels = ["A1"; "A2"; "B1"; "B2"; "C1"; "C2"; "D1"], + linkedNodeIds = [ + 0,2 + 0,3 + 1,3 + 2,4 + 3,4 + 3,5 + 4,6 + 5,6 + ], + NodeOutlineColor = Color.fromKeyword Black, + NodeOutlineWidth = 1., + linkValues = [8; 4; 2; 7; 3; 2; 5; 2], + LinkColor = Color.fromColors [ + Color.fromHex "#828BFB" + Color.fromHex "#828BFB" + Color.fromHex "#F27762" + Color.fromHex "#33D6AB" + Color.fromHex "#BC82FB" + Color.fromHex "#BC82FB" + Color.fromHex "#FFB47B" + Color.fromHex "#47DCF5" + ], + LinkOutlineColor = Color.fromKeyword Black, + LinkOutlineWidth = 1., + UseDefaults = false + ) + + +module Table = + + let ``Simple table chart`` = + let header = ["RowIndex";"A";"simple";"table"] + let rows = + [ + ["0";"I" ;"am" ;"a"] + ["1";"little";"example";"!"] + ] + Chart.Table(headerValues = header, cellsValues = rows, UseDefaults = false) + + let ``Styled table chart`` = + let header = ["RowIndex";"A";"simple";"table"] + let rows = + [ + ["0";"I" ;"am" ;"a"] + ["1";"little";"example";"!"] + ] + Chart.Table( + headerValues = header, + cellsValues = rows, + HeaderAlign = StyleParam.HorizontalAlign.Center, + CellsMultiAlign = [StyleParam.HorizontalAlign.Left; StyleParam.HorizontalAlign.Center; StyleParam.HorizontalAlign.Right], + HeaderFillColor = Color.fromString "#45546a", + CellsFillColor = Color.fromColors [Color.fromString "#deebf7"; Color.fromString "lightgrey"; Color.fromString "#deebf7"; Color.fromString "lightgrey"], + HeaderHeight = 30, + HeaderOutlineColor = Color.fromString "black", + HeaderOutlineWidth = 2., + MultiColumnWidth = [70.; 50.; 100.; 70.], + ColumnOrder = [1; 2; 3; 4], + UseDefaults = false + ) + + let ``Cell color dependent table chart`` = + let header2 = ["Identifier";"T0";"T1";"T2";"T3"] + let rowvalues = + [ + [10001.;0.2;2.0;4.0;5.0] + [10002.;2.1;2.0;1.8;2.1] + [10003.;4.5;3.0;2.0;2.5] + [10004.;0.0;0.1;0.3;0.2] + [10005.;1.0;1.6;1.8;2.2] + [10006.;1.0;0.8;1.5;0.7] + [10007.;2.0;2.0;2.1;1.9] + ] + |> Seq.sortBy (fun x -> x.[1]) + + //map color from value to hex representation + let mapColor min max value = + let proportion = + (255. * (value - min) / (max - min)) + |> int + Color.fromRGB 255 (255 - proportion) proportion + + //Assign a color to every cell seperately. Matrix must be transposed for correct orientation. + let cellcolor = + rowvalues + |> Seq.map (fun row -> + row + |> Seq.mapi (fun index value -> + if index = 0 then Color.fromString "white" + else mapColor 0. 5. value + ) + ) + |> Seq.transpose + |> Seq.map Color.fromColors + |> Color.fromColors + + Chart.Table( + headerValues = header2, + cellsValues = rowvalues, + CellsFillColor=cellcolor, + UseDefaults = false + ) + + + let ``Sequence representation table chart`` = + let sequence = + [ + "ATGAGACGTCGAGACTGATAGACGTCGATAGACGTCGATAGACCG" + "ATAGACTCGTGATAGACGTCGATAGACGTCGATAGAGTATAGACC" + "GTGATAGACGTCGAGAAGACGTCGATAGACGTCGATAGACGTCGA" + "TAGAGATAGACGTCGATAGACCGTATAGAAGACGTCGATAGATAG" + "ACGTCGATAGACCGTAGACGTCGATAGACGTCGATAGACCGT" + ] + |> String.concat "" + + let elementsPerRow = 60 + + let headers = + [0..elementsPerRow] + |> Seq.map (fun x -> + if x%10=0 && x <> 0 then "|" + else "" + ) + + let cells = + sequence + |> Seq.chunkBySize elementsPerRow + |> Seq.mapi (fun i x -> Seq.append [string (i * elementsPerRow)] (Seq.map string x)) + + let cellcolors = + cells + |> Seq.map (fun row -> + row + |> Seq.map (fun element -> + match element with + //colors taken from DRuMS + //(http://biomodel.uah.es/en/model4/dna/atgc.htm) + | "A" -> Color.fromString "#5050FF" + | "T" -> Color.fromString "#E6E600" + | "G" -> Color.fromString "#00C000" + | "C" -> Color.fromString "#E00000" + | "U" -> Color.fromString "#B48100" + | _ -> Color.fromString "white" + ) + ) + |> Seq.transpose + |> Seq.map (fun x -> Seq.append x (seq [Color.fromString "white"])) + |> Seq.map Color.fromColors + |> Color.fromColors + + let line = Line.init(Width = 0., Color = Color.fromString "white") + let chartwidth = 50 + 10 * elementsPerRow + + Chart.Table( + headerValues = headers, + cellsValues = cells, + CellsOutline = line, + HeaderOutline = line, + CellsHeight = 20, + MultiColumnWidth = [50.;10.], + CellsMultiAlign = [StyleParam.HorizontalAlign.Right;StyleParam.HorizontalAlign.Center], + CellsFillColor = cellcolors, + UseDefaults = false + ) + |> Chart.withSize(Width=chartwidth) + |> Chart.withTitle "Sequence A" + +module Indicator = + + open Plotly.NET.TraceObjects + open Plotly.NET.LayoutObjects + + let ``Angular gauge indicator`` = + ChartDomain.Chart.Indicator( + value = 200., + mode = StyleParam.IndicatorMode.NumberDeltaGauge, + Delta = IndicatorDelta.init(Reference=160), + Range = StyleParam.Range.MinMax(0., 250.), + Domain = Domain.init(Row = 0, Column = 0), + UseDefaults = false + ) + + let ``Bullet gauge indicator`` = + Chart.Indicator( + value = 120, + mode = StyleParam.IndicatorMode.NumberDeltaGauge, + DeltaReference = 90, + Range = StyleParam.Range.MinMax(-200., 200.), + GaugeShape = StyleParam.IndicatorGaugeShape.Bullet, + ShowGaugeAxis = false, + Domain = Domain.init(Row = 0, Column = 1), + UseDefaults = false + ) + + let ``Delta indicator with reference`` = + Chart.Indicator( + value = "300", + mode = StyleParam.IndicatorMode.NumberDelta, + DeltaReference = 90., + Domain = Domain.init(Row = 1, Column = 0), + UseDefaults = false + ) + + let ``Delta indicator`` = + Chart.Indicator( + value = 40., + mode = StyleParam.IndicatorMode.Delta, + DeltaReference = 90., + Domain = Domain.init(Row = 1, Column = 1), + UseDefaults = false + ) + + +module Icicle = + + let ``Simple icicle chart`` = + Chart.Icicle( + labels = ["Eve"; "Cain"; "Seth"; "Enos"; "Noam"; "Abel"; "Awan"; "Enoch"; "Azura"], + parents = [""; "Eve"; "Eve"; "Seth"; "Seth"; "Eve"; "Eve"; "Awan"; "Eve"], + ShowSectionColorScale = true, + SectionColorScale = StyleParam.Colorscale.Viridis, + TilingOrientation = StyleParam.Orientation.Vertical, + TilingFlip = StyleParam.TilingFlip.Y, + PathBarEdgeShape = StyleParam.PathbarEdgeShape.BackSlash, + UseDefaults = false + ) + + let ``Styled icicle chart`` = + let labelsParents = [ + ("A",""), 20 + ("B",""), 1 + ("C",""), 2 + ("D",""), 3 + ("E",""), 4 + + ("AA","A"), 15 + ("AB","A"), 5 + + ("BA","B"), 1 + + ("AAA", "AA"), 10 + ("AAB", "AA"), 5 + ] + + Chart.Icicle( + labelsparents = (labelsParents |> Seq.map fst), + Values = (labelsParents |> Seq.map snd), + BranchValues = StyleParam.BranchValues.Total, // branch values are the total of their childrens values + SectionColorScale = StyleParam.Colorscale.Viridis, + ShowSectionColorScale = true, + SectionOutlineColor = Color.fromKeyword Black, + Tiling = IcicleTiling.init(Flip = StyleParam.TilingFlip.XY), + UseDefaults = false + ) \ No newline at end of file diff --git a/tests/Common/FSharpTestBase/TestCharts/ChartMapTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/ChartMapTestCharts.fs new file mode 100644 index 000000000..d2b734cd7 --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/ChartMapTestCharts.fs @@ -0,0 +1,250 @@ +module ChartMapTestCharts + +open Plotly.NET +open Plotly.NET.TraceObjects +open Plotly.NET.LayoutObjects +open System + +open TestUtils.DataGeneration +open Deedle + +module GeoBaseMap = + + let ``Base map only`` = + Chart.PointGeo(locations = [], UseDefaults = false) // deliberately empty chart to show the base map only + |> Chart.withMarginSize(0, 0, 0, 0) + + let ``styled base map only`` = + let myGeo = + Geo.init( + Resolution=StyleParam.GeoResolution.R50, + ShowCoastLines=true, + CoastLineColor=Color.fromString "RebeccaPurple", + ShowLand=true, + LandColor=Color.fromString "LightGreen", + ShowOcean=true, + OceanColor=Color.fromString "LightBlue", + ShowLakes=true, + LakeColor=Color.fromString "Blue", + ShowRivers=true, + RiverColor=Color.fromString "Blue" + ) + Chart.PointGeo(locations = [], UseDefaults = false) + |> Chart.withGeo myGeo + |> Chart.withMarginSize(0, 0, 0, 0) + + let ``Base map country borders at 1:50m resolution`` = + let countryGeo = + Geo.init( + Visible=false, + Resolution=StyleParam.GeoResolution.R50, + ShowCountries=true, + CountryColor=Color.fromString "RebeccaPurple" + ) + Chart.PointGeo(locations = [], UseDefaults = false) + |> Chart.withGeo countryGeo + |> Chart.withMarginSize(0, 0, 0, 0) + +module ChoroplethMap = + + let ``ChoroplethMap chart of top beverage consumption countries`` = + let locations,z = + beverageConsumptionLocationData + |> List.unzip + + Chart.ChoroplethMap( + locations = locations,z = z, + LocationMode=StyleParam.LocationFormat.CountryNames, + UseDefaults = false + ) + + let ``ChoroplethMap chart of top beverage consumption countries with styled map and projecton`` = + let locations,z = + beverageConsumptionLocationData + |> List.unzip + + Chart.ChoroplethMap( + locations = locations,z = z, + LocationMode=StyleParam.LocationFormat.CountryNames, + UseDefaults = false + ) + |> Chart.withGeoStyle( + Projection=GeoProjection.init(projectionType=StyleParam.GeoProjectionType.Mollweide), + ShowLakes=true, + ShowOcean=true, + OceanColor= Color.fromString "lightblue", + ShowRivers=true) + |> Chart.withColorBarStyle ( + TitleText="Alcohol consumption[l/y]",Len=0.5 + ) + +module ScatterGeo = () + +module PointGeo = + + let ``Point geo chart of canadian cities`` = + + let cityNames = [ + "Montreal"; "Toronto"; "Vancouver"; "Calgary"; "Edmonton"; + "Ottawa"; "Halifax"; "Victoria"; "Winnepeg"; "Regina" + ] + + let lon = [ + -73.57; -79.24; -123.06; -114.1; -113.28; + -75.43; -63.57; -123.21; -97.13; -104.6 + ] + let lat = [ + 45.5; 43.4; 49.13; 51.1; 53.34; 45.24; + 44.64; 48.25; 49.89; 50.45 + ] + Chart.PointGeo( + longitudes = lon, + latitudes = lat, + MultiText=cityNames, + TextPosition=StyleParam.TextPosition.TopCenter, + UseDefaults = false + ) + |> Chart.withGeoStyle( + Scope=StyleParam.GeoScope.NorthAmerica, + Projection=GeoProjection.init(StyleParam.GeoProjectionType.AzimuthalEqualArea), + CountryColor = Color.fromString "lightgrey" + ) + |> Chart.withMarginSize(0, 0, 0, 0) + + +module LineGeo = + + let ``LineGeo plot of feb. 2011 American Airline flights`` = + let data = + FlightData + |> fun csv -> Frame.ReadCsvString(csv,true,separators=",") + + let opacityVals : float [] = data.["cnt"] |> Series.values |> fun s -> s |> Seq.map (fun v -> v/(Seq.max s)) |> Array.ofSeq + let startCoords = Series.zipInner data.["start_lon"] data.["start_lat"] + let endCoords = Series.zipInner data.["end_lon"] data.["end_lat"] + let coords = Series.zipInner startCoords endCoords |> Series.values + + coords + |> Seq.mapi (fun i (startCoords,endCoords) -> + Chart.LineGeo( + lonlat = [startCoords; endCoords], + Opacity = opacityVals.[i], + MarkerColor = Color.fromString "red", + UseDefaults = false + ) + ) + |> Chart.combine + |> Chart.withLegend(false) + |> Chart.withGeoStyle( + Scope=StyleParam.GeoScope.NorthAmerica, + Projection=GeoProjection.init(StyleParam.GeoProjectionType.AzimuthalEqualArea), + ShowLand=true, + LandColor = Color.fromString "lightgrey" + ) + |> Chart.withMarginSize(0,0,50,0) + |> Chart.withTitle "Feb. 2011 American Airline flights" + +module MapboxBaseMap = + let ``Open streetmap layer only`` = + + Chart.PointMapbox( + longitudes = [], + latitudes = [], + UseDefaults = false + ) + // open street map is default layer + +module ScatterMapbox = () + +module PointMapbox = + + let ``Point mapbox chart of canadian cities`` = + let cityNames = [ + "Montreal"; "Toronto"; "Vancouver"; "Calgary"; "Edmonton"; + "Ottawa"; "Halifax"; "Victoria"; "Winnepeg"; "Regina" + ] + + let lon = [ + -73.57; -79.24; -123.06; -114.1; -113.28; + -75.43; -63.57; -123.21; -97.13; -104.6 + ] + + let lat = [ + 45.5; 43.4; 49.13; 51.1; 53.34; 45.24; + 44.64; 48.25; 49.89; 50.45 + ] + + Chart.PointMapbox( + longitudes = lon, + latitudes = lat, + MultiText = cityNames, + TextPosition = StyleParam.TextPosition.TopCenter, + UseDefaults = false + ) + |> Chart.withMapbox( + Mapbox.init( + Center=(-104.6,50.45) + ) + ) + +module LineMapbox = + + let ``LineMapbox plot of feb. 2011 American Airline flights`` = + + let data = + FlightData + |> fun csv -> Frame.ReadCsvString(csv,true,separators=",") + + let opacityVals : float [] = data.["cnt"] |> Series.values |> fun s -> s |> Seq.map (fun v -> v/(Seq.max s)) |> Array.ofSeq + let startCoords = Series.zipInner data.["start_lon"] data.["start_lat"] + let endCoords = Series.zipInner data.["end_lon"] data.["end_lat"] + let coords = Series.zipInner startCoords endCoords |> Series.values + + coords + |> Seq.mapi (fun i (startCoords,endCoords) -> + Chart.LineMapbox( + lonlat = [startCoords; endCoords], + Opacity = opacityVals.[i], + LineColor = Color.fromString "red", + UseDefaults = false + ) + ) + |> Chart.combine + |> Chart.withLegend(false) + |> Chart.withMapbox( + Mapbox.init( + Center=(-97.0372,32.8959) + ) + ) + |> Chart.withMarginSize(0,0,50,0) + |> Chart.withTitle "Feb. 2011 American Airline flights" + + +module BubbleMapbox = () + +module ChoroplethMapbox = () + +module DensityMapbox = + + let ``Density mapbox chart of earthquakes`` = + let dataDensityMapbox = + EarthquakeData + |> fun d -> Frame.ReadCsvString(d,true,separators=",") + + let lon = dataDensityMapbox.["Longitude"] |> Series.values + let lat= dataDensityMapbox.["Latitude"] |> Series.values + let magnitudes = dataDensityMapbox.["Magnitude"] |> Series.values + Chart.DensityMapbox( + longitudes = lon, + latitudes = lat, + Z = magnitudes, + Radius=8, + ColorScale=StyleParam.Colorscale.Viridis, + UseDefaults = false + ) + |> Chart.withMapbox( + Mapbox.init( + Style = StyleParam.MapboxStyle.StamenTerrain, + Center = (60.,30.) + ) + ) diff --git a/tests/Common/FSharpTestBase/TestCharts/ChartPolarTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/ChartPolarTestCharts.fs new file mode 100644 index 000000000..e512cf18f --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/ChartPolarTestCharts.fs @@ -0,0 +1,62 @@ +module ChartPolarTestCharts + +open Plotly.NET +open Plotly.NET.TraceObjects +open Plotly.NET.LayoutObjects +open System + +open TestUtils.DataGeneration +open Deedle + +let internal radial = [ 1; 2; 3; 4; 5; 6; 7;] +let internal theta = [0; 45; 90; 135; 200; 320; 184;] + +module ScatterPolar = () + +module PointPolar = + + let ``Simple polar point chart`` = Chart.PointPolar(r = radial,theta = theta, UseDefaults = false) + +module LinePolar = + + let ``Simple polar line chart with line style`` = + Chart.LinePolar(r = radial,theta = theta, UseDefaults = false) + |> Chart.withLineStyle(Color=Color.fromString "purple",Dash=StyleParam.DrawingStyle.DashDot) + +module SplinePolar = + + let ``styled polar spline chart`` = + Chart.SplinePolar( + r = radial, + theta = theta, + MultiText=["one";"two";"three";"four";"five";"six";"seven"], + TextPosition=StyleParam.TextPosition.TopCenter, + ShowMarkers=true, + UseDefaults = false + ) + +module BubblePolar = () + +module BarPolar = + + let ``Styled windrose chart`` = + let r = [77.5; 72.5; 70.0; 45.0; 22.5; 42.5; 40.0; 62.5] + let r2 = [57.5; 50.0; 45.0; 35.0; 20.0; 22.5; 37.5; 55.0] + let r3 = [40.0; 30.0; 30.0; 35.0; 7.5; 7.5; 32.5; 40.0] + let r4 = [20.0; 7.5; 15.0; 22.5; 2.5; 2.5; 12.5; 22.5] + + let t = ["North"; "N-E"; "East"; "S-E"; "South"; "S-W"; "West"; "N-W"] + + [ + Chart.BarPolar (r = r , theta = t, Name="11-14 m/s" , MarkerPatternShape = StyleParam.PatternShape.Checked , UseDefaults = false) + Chart.BarPolar (r = r2, theta = t, Name="8-11 m/s" , MarkerPatternShape = StyleParam.PatternShape.DiagonalChecked, UseDefaults = false) + Chart.BarPolar (r = r3, theta = t, Name="5-8 m/s" , MarkerPatternShape = StyleParam.PatternShape.VerticalLines , UseDefaults = false) + Chart.BarPolar (r = r4, theta = t, Name="< 5 m/s" , MarkerPatternShape = StyleParam.PatternShape.HorizontalLines, UseDefaults = false) + ] + |> Chart.combine + |> Chart.withAngularAxis( + AngularAxis.init( + CategoryOrder = StyleParam.CategoryOrder.Array, + CategoryArray = (["East"; "N-E"; "North"; "N-W"; "West"; "S-W"; "South"; "S-E";]) // set the order of the categorical axis + ) + ) \ No newline at end of file diff --git a/tests/Common/FSharpTestBase/TestCharts/ChartSmithTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/ChartSmithTestCharts.fs new file mode 100644 index 000000000..2cd6b9e73 --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/ChartSmithTestCharts.fs @@ -0,0 +1,49 @@ +module ChartSmithTestCharts + +open Plotly.NET +open Plotly.NET.TraceObjects + + +module ScatterSmith = + + let ``Simple smith scatter chart`` = + Chart.ScatterSmith( + real = [0.5; 1.; 2.; 3.], + imag = [0.5; 1.; 2.; 3.], + mode = StyleParam.Mode.Lines_Markers_Text, + MultiText = ["Pretty"; "Cool"; "Plot"; "Huh?"], + TextPosition = StyleParam.TextPosition.TopCenter, + UseDefaults = false + ) + +module PointSmith = + + let ``Simple smith point chart`` = + Chart.PointSmith( + real = [0.5; 1.; 2.; 3.], + imag = [0.5; 1.; 2.; 3.], + UseDefaults = false + ) + +module LineSmith = + + let ``Simple smith line chart`` = + Chart.LineSmith( + real = [0.5; 1.; 2.; 3.], + imag = [0.5; 1.; 2.; 3.], + LineDash = StyleParam.DrawingStyle.DashDot, + LineColor = Color.fromKeyword Purple, + UseDefaults = false + ) + +module BubbleSmith = + + let ``Simple smith bubble chart`` = + Chart.BubbleSmith( + real = [0.5; 1.; 2.; 3.], + imag = [0.5; 1.; 2.; 3.], + sizes = [10;20;30;40], + MultiText=["one";"two";"three";"four";"five";"six";"seven"], + TextPosition=StyleParam.TextPosition.TopCenter, + UseDefaults = false + ) \ No newline at end of file diff --git a/tests/Common/FSharpTestBase/TestCharts/ChartTernaryTestCharts.fs b/tests/Common/FSharpTestBase/TestCharts/ChartTernaryTestCharts.fs new file mode 100644 index 000000000..622130e9e --- /dev/null +++ b/tests/Common/FSharpTestBase/TestCharts/ChartTernaryTestCharts.fs @@ -0,0 +1,40 @@ +module ChartTernaryTestCharts + +open Plotly.NET +open Plotly.NET.TraceObjects +open Plotly.NET.LayoutObjects +open System + +open TestUtils.DataGeneration +open Deedle + +module ScatterTernary = () + +module PointTernary = + + let ``Styled ternary point chart`` = + Chart.PointTernary(abc = [1,2,3], UseDefaults = false) + |> Chart.withAAxis(LinearAxis.init(Title = Title.init("A"), Color = Color.fromKeyword ColorKeyword.DarkOrchid)) + |> Chart.withBAxis(LinearAxis.init(Title = Title.init("B"), Color = Color.fromKeyword ColorKeyword.DarkRed)) + |> Chart.withCAxis(LinearAxis.init(Title = Title.init("C"), Color = Color.fromKeyword ColorKeyword.DarkCyan)) + +module LineTernary = + + let ``Styled ternary line chart`` = + Chart.LineTernary( + A = [10; 20; 30; 40; 50; 60; 70; 80;], + B = ([10; 20; 30; 40; 50; 60; 70; 80;] |> List.rev), + Sum = 100, + ShowMarkers = true, + LineDash = StyleParam.DrawingStyle.DashDot, + UseDefaults = false + ) + |> Chart.withTernary( + Ternary.init( + AAxis = LinearAxis.init(Color = Color.fromKeyword ColorKeyword.DarkOrchid), + BAxis = LinearAxis.init(Color = Color.fromKeyword ColorKeyword.DarkRed), + CAxis = LinearAxis.init(Color = Color.fromKeyword ColorKeyword.DarkCyan) + ) + ) + +module BubbleTernary = () \ No newline at end of file diff --git a/tests/Common/FSharpTestBase/TestUtils.fs b/tests/Common/FSharpTestBase/TestUtils.fs new file mode 100644 index 000000000..b8460a27c --- /dev/null +++ b/tests/Common/FSharpTestBase/TestUtils.fs @@ -0,0 +1,213 @@ +module TestUtils + +open System.Reflection +open System.IO +open Expecto +open Plotly.NET +open DynamicObj +open Newtonsoft.Json + +module DataGeneration = + + //---------------------- Generate linearly spaced vector ---------------------- + let linspace (min,max,n) = + if n <= 2 then failwithf "n needs to be larger then 2" + let bw = float (max - min) / (float n - 1.) + Array.init n (fun i -> min + (bw * float i)) + + //-------------------- Generate linearly spaced mesh grid --------------------- + let mgrid (min,max,n) = + + let data = linspace(min,max,n) + + let z = [|for i in 1 .. n do [|for i in 1 .. n do yield data|]|] + let x = [|for i in 1 .. n do [|for j in 1 .. n do yield [|for k in 1 .. n do yield data.[i-1]|]|]|] + let y = [|for i in 1 .. n do [|for j in 1 .. n do yield [|for k in 1 .. n do yield data.[j-1]|]|]|] + + x,y,z + + let normal (rnd:System.Random) mu tau = + let mutable v1 = 2.0 * rnd.NextDouble() - 1.0 + let mutable v2 = 2.0 * rnd.NextDouble() - 1.0 + let mutable r = v1 * v1 + v2 * v2 + while (r >= 1.0 || r = 0.0) do + v1 <- 2.0 * rnd.NextDouble() - 1.0 + v2 <- 2.0 * rnd.NextDouble() - 1.0 + r <- v1 * v1 + v2 * v2 + let fac = sqrt(-2.0*(log r)/r) + (tau * v1 * fac + mu) + + // this is not the original string, but its few first entries + let [] FlightData = """start_lat,start_lon,end_lat,end_lon,airline,airport1,airport2,cnt +32.89595056,-97.0372,35.04022222,-106.6091944,AA,DFW,ABQ,444 +41.979595,-87.90446417,30.19453278,-97.66987194,AA,ORD,AUS,166 +32.89595056,-97.0372,41.93887417,-72.68322833,AA,DFW,BDL,162 +18.43941667,-66.00183333,41.93887417,-72.68322833,AA,SJU,BDL,56 +32.89595056,-97.0372,33.56294306,-86.75354972,AA,DFW,BHM,168 +25.79325,-80.29055556,36.12447667,-86.67818222,AA,MIA,BNA,56 +32.89595056,-97.0372,42.3643475,-71.00517917,AA,DFW,BOS,422 +25.79325,-80.29055556,42.3643475,-71.00517917,AA,MIA,BOS,392 +41.979595,-87.90446417,42.3643475,-71.00517917,AA,ORD,BOS,430 +18.43941667,-66.00183333,42.3643475,-71.00517917,AA,SJU,BOS,56 +18.33730556,-64.97336111,42.3643475,-71.00517917,AA,STT,BOS,44 +25.79325,-80.29055556,39.17540167,-76.66819833,AA,MIA,BWI,112""" + + let [] EarthquakeData ="""Date,Latitude,Longitude,Magnitude +01/02/1965,19.246,145.616,6.0 +01/04/1965,1.8630000000000002,127.352,5.8 +01/05/1965,-20.579,-173.972,6.2 +01/08/1965,-59.076,-23.557,5.8 +01/09/1965,11.937999999999999,126.427,5.8 +01/10/1965,-13.405,166.62900000000002,6.7 +01/12/1965,27.357,87.867,5.9 +01/15/1965,-13.309000000000001,166.21200000000002,6.0 +01/16/1965,-56.452,-27.043000000000003,6.0 +01/17/1965,-24.563000000000002,178.487,5.8 +01/17/1965,-6.807,108.988,5.9 +01/24/1965,-2.608,125.95200000000001,8.2 +01/29/1965,54.636,161.703,5.5 +02/01/1965,-18.697,-177.864,5.6 +02/02/1965,37.523,73.251,6.0 +02/04/1965,-51.84,139.741,6.1 +02/04/1965,51.251000000000005,178.715,8.7 +02/04/1965,51.638999999999996,175.055,6.0 +02/04/1965,52.528,172.007,5.7 +02/04/1965,51.626000000000005,175.74599999999998,5.8 +02/04/1965,51.037,177.84799999999998,5.9 +02/04/1965,51.73,173.975,5.9 +02/04/1965,51.775,173.058,5.7 +02/04/1965,52.611000000000004,172.588,5.7 +02/04/1965,51.831,174.368,5.7 +02/04/1965,51.948,173.96900000000002,5.6 +02/04/1965,51.443000000000005,179.605,7.3 +02/04/1965,52.773,171.97400000000002,6.5 +02/04/1965,51.772,174.696,5.6 +02/04/1965,52.975,171.09099999999998,6.4 +02/04/1965,52.99,170.87400000000002,5.8 +02/04/1965,51.536,175.045,5.8 +02/04/1965,13.245,-44.922,5.8 +02/04/1965,51.812,174.206,5.7 +02/05/1965,51.762,174.84099999999998,5.7 +02/05/1965,52.438,174.321,6.3 +02/05/1965,51.946000000000005,173.84,5.7 +02/05/1965,51.738,174.56599999999997,6.0 +02/05/1965,51.486999999999995,176.558,5.6 +02/06/1965,53.008,-162.00799999999998,6.4 +02/06/1965,52.184,175.505,6.2 +02/06/1965,52.076,172.918,5.6 +02/06/1965,51.744,175.213,5.7 +02/06/1965,52.056999999999995,174.11599999999999,5.7 +02/06/1965,53.191,-161.859,6.3 +02/06/1965,51.446999999999996,176.46900000000002,5.8 +02/07/1965,51.258,173.393,5.7 +02/07/1965,52.031000000000006,175.41099999999997,5.7 +02/07/1965,51.294,179.092,5.8 +02/08/1965,55.223,165.426,5.9 +02/09/1965,-18.718,169.386,5.6 +02/09/1965,52.815,171.90400000000002,6.0 +02/12/1965,52.188,172.752,5.8 +02/15/1965,51.00899999999999,179.325,5.8 +02/15/1965,3.0260000000000002,125.95100000000001,5.9 +02/16/1965,38.908,142.095,5.7 +02/17/1965,51.693999999999996,176.446,5.7 +02/17/1965,21.526999999999997,143.08100000000002,5.6 +02/18/1965,25.011,94.186,5.6""" + + + let beverageConsumptionLocationData = [ + ("Belarus",17.5); ("Moldova",16.8);("Lithuania",15.4);("Russia",15.1); + ("Romania",14.4);("Ukraine",13.9);("Andorra",13.8);("Hungary",13.3); + ("Czech Republic",13.);("Slovakia",13.);("Portugal",12.9);("Serbia",12.6); + ("Grenada",12.5);("Poland",12.5);("Latvia",12.3);("Finland",12.3); + ("South Korea",12.3);("France",12.2);("Australia",12.2);("Croatia",12.2); + ("Ireland",11.9);("Luxembourg",11.9);("Germany",11.8);("Slovenia",11.6); + ("United Kingdom",11.6);("Denmark",11.4);("Bulgaria",11.4);("Spain",11.2); + ("Belgium",11.);("South Africa",11.);("New Zealand",10.9);("Gabon",10.9); + ("Namibia",10.8);("Switzerland",10.7);("Saint Lucia",10.4);("Austria",10.3); + ("Estonia",10.3);("Greece",10.3);("Kazakhstan",10.3);("Canada",10.2); + ("Nigeria",10.1);("Netherlands",9.9);("Uganda",9.8);("Rwanda",9.8); + ("Chile",9.6);("Argentina",9.3);("Burundi",9.3);("United States",9.2); + ("Cyprus",9.2);("Sweden",9.2);("Venezuela",8.9);("Paraguay",8.8);("Brazil",8.7); + ("Sierra Leone",8.7);("Montenegro",8.7);("Belize",8.5);("Cameroon",8.4); + ("Botswana",8.4);("Saint Kitts and Nevis",8.2);("Guyana",8.1);("Peru",8.1); + ("Panama",8.);("Niue",8.);("Palau",7.9);("Norway",7.7);("Tanzania",7.7); + ("Georgia",7.7);("Uruguay",7.6);("Angola",7.5);("Laos",7.3);("Japan",7.2); + ("Mexico",7.2);("Ecuador",7.2);("Dominica",7.1);("Iceland",7.1); + ("Thailand",7.1);("Bosnia and Herzegovina",7.1);("Sao Tome and Principe",7.1); + ("Malta",7.);("Albania",7.);("Bahamas",6.9);("Dominican Republic",6.9); + ("Mongolia",6.9);("Cape Verde",6.9);("Barbados",6.8);("Burkina Faso",6.8); + ("Italy",6.7);("Trinidad and Tobago",6.7);("China",6.7);("Macedonia",6.7); + ("Saint Vincent and the Grenadines",6.6);("Equatorial Guinea",6.6); + ("Suriname",6.6);("Vietnam",6.6);("Lesotho",6.5);("Haiti",6.4); + ("Cook Islands",6.4);("Colombia",6.2);("Ivory Coast",6.);("Bolivia",5.9); + ("Swaziland",5.7);("Zimbabwe",5.7);("Seychelles",5.6);("Cambodia",5.5); + ("Puerto Rico",5.4);("Netherlands Antilles",5.4);("Philippines",5.4); + ("Costa Rica",5.4);("Armenia",5.3);("Cuba",5.2);("Nicaragua",5.); + ("Jamaica",4.9);("Ghana",4.8);("Liberia",4.7);("Uzbekistan",4.6); + ("Chad",4.4);("United Arab Emirates",4.3);("Kyrgyzstan",4.3);("India",4.3); + ("Turkmenistan",4.3);("Kenya",4.3);("Ethiopia",4.2);("Honduras",4.); + ("Guinea-Bissau",4.);("Zambia",4.);("Republic of the Congo",3.9);("Guatemala",3.8); + ("Central African Republic",3.8);("North Korea",3.7);("Sri Lanka",3.7); + ("Mauritius",3.6);("Samoa",3.6);("Democratic Republic of the Congo",3.6); + ("Nauru",3.5);("Gambia",3.4);("Federated States of Micronesia",3.3); + ("El Salvador",3.2);("Fiji",3.);("Papua New Guinea",3.);("Kiribati",3.); + ("Tajikistan",2.8);("Israel",2.8);("Sudan",2.7);("Malawi",2.5);("Lebanon",2.4); + ("Azerbaijan",2.3);("Mozambique",2.3);("Togo",2.3);("Nepal",2.2);("Brunei",2.1); + ("Benin",2.1);("Singapore",2.);("Turkey",2.);("Madagascar",1.8);("Solomon Islands",1.7); + ("Tonga",1.6);("Tunisia",1.5);("Tuvalu",1.5);("Qatar",1.5);("Vanuatu",1.4); + ("Djibouti",1.3);("Malaysia",1.3);("Syria",1.2);("Maldives",1.2);("Mali",1.1); + ("Eritrea",1.1);("Algeria",1.);("Iran",1.);("Oman",0.9);("Brunei",0.9); + ("Morocco",0.9);("Jordan",0.7);("Bhutan",0.7);("Guinea",0.7);("Burma",0.7); + ("Afghanistan",0.7);("Senegal",0.6);("Indonesia",0.6);("Timor-Leste",0.6); + ("Iraq",0.5);("Somalia",0.5);("Egypt",0.4);("Niger",0.3);("Yemen",0.3); + ("Comoros",0.2);("Saudi Arabia",0.2);("Bangladesh",0.2);("Kuwait",0.1); + ("Libya",0.1);("Mauritania",0.1);("Pakistan",0.1); + ] + +module HtmlCodegen = + + let getLogoPNG() = + let assembly = Assembly.GetExecutingAssembly() + use str = assembly.GetManifestResourceStream($"FSharpTestBase.logo.png") + use r = new BinaryReader(str) + r.ReadBytes(int(str.Length)) + |> System.Convert.ToBase64String + + let getFullPlotlyJS() = + let assembly = Assembly.GetExecutingAssembly() + use str = assembly.GetManifestResourceStream($"FSharpTestBase.plotly-{Globals.PLOTLYJS_VERSION}.min.js") + use r = new StreamReader(str) + r.ReadToEnd() + + let substringIsInChart chart htmlizer substring = + chart + |> htmlizer + |> Expect.stringContains + |> (fun expecting -> expecting substring $"Should've contained {substring}") + + + let substringListIsInChart chart htmlizer substringList = + for substring in substringList do + substringIsInChart chart htmlizer substring + + + let chartGeneratedContains chart substring = + substringIsInChart chart GenericChart.toChartHTML substring + substringIsInChart chart GenericChart.toEmbeddedHTML substring + + + let chartGeneratedContainsList chart substringList = + for substring in substringList do + chartGeneratedContains chart substring + + let emptyLayout chart = + "var layout = {};" |> chartGeneratedContains chart + +module Objects = + + let jsonFieldIsSetWith fieldName expected (object:#DynamicObj) = + Expect.equal + ((object :> DynamicObj)?($"{fieldName}") |> JsonConvert.SerializeObject) + expected + ($"Field `{fieldName}` not set correctly in serialized dynamic object.") + diff --git a/tests/Plotly.NET.Tests.CSharpConsole/Plotly.NET.Tests.CSharpConsole.csproj b/tests/ConsoleApps/CSharpConsole/CSharpConsole.csproj similarity index 57% rename from tests/Plotly.NET.Tests.CSharpConsole/Plotly.NET.Tests.CSharpConsole.csproj rename to tests/ConsoleApps/CSharpConsole/CSharpConsole.csproj index 25b6015ec..9e3def058 100644 --- a/tests/Plotly.NET.Tests.CSharpConsole/Plotly.NET.Tests.CSharpConsole.csproj +++ b/tests/ConsoleApps/CSharpConsole/CSharpConsole.csproj @@ -1,13 +1,14 @@ - - - - Exe net6.0 false + + + + + diff --git a/tests/Plotly.NET.Tests.CSharpConsole/Program.cs b/tests/ConsoleApps/CSharpConsole/Program.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpConsole/Program.cs rename to tests/ConsoleApps/CSharpConsole/Program.cs diff --git a/tests/Plotly.NET.Tests.FSharpConsole/Plotly.NET.Tests.FSharpConsole.fsproj b/tests/ConsoleApps/FSharpConsole/FSharpConsole.fsproj similarity index 83% rename from tests/Plotly.NET.Tests.FSharpConsole/Plotly.NET.Tests.FSharpConsole.fsproj rename to tests/ConsoleApps/FSharpConsole/FSharpConsole.fsproj index e4960d3f4..274427e7c 100644 --- a/tests/Plotly.NET.Tests.FSharpConsole/Plotly.NET.Tests.FSharpConsole.fsproj +++ b/tests/ConsoleApps/FSharpConsole/FSharpConsole.fsproj @@ -13,7 +13,7 @@ - + diff --git a/tests/Plotly.NET.Tests.FSharpConsole/Program.fs b/tests/ConsoleApps/FSharpConsole/Program.fs similarity index 100% rename from tests/Plotly.NET.Tests.FSharpConsole/Program.fs rename to tests/ConsoleApps/FSharpConsole/Program.fs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/APITest/Chart2DAPITest.cs b/tests/CoreTests/CSharpInteroperabilityTests/APITest/Chart2DAPITest.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/APITest/Chart2DAPITest.cs rename to tests/CoreTests/CSharpInteroperabilityTests/APITest/Chart2DAPITest.cs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/APITest/Chart3DAPITest.cs b/tests/CoreTests/CSharpInteroperabilityTests/APITest/Chart3DAPITest.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/APITest/Chart3DAPITest.cs rename to tests/CoreTests/CSharpInteroperabilityTests/APITest/Chart3DAPITest.cs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/APITest/ChartDomainAPITest.cs b/tests/CoreTests/CSharpInteroperabilityTests/APITest/ChartDomainAPITest.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/APITest/ChartDomainAPITest.cs rename to tests/CoreTests/CSharpInteroperabilityTests/APITest/ChartDomainAPITest.cs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/APITest/ChartMapAPITest.cs b/tests/CoreTests/CSharpInteroperabilityTests/APITest/ChartMapAPITest.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/APITest/ChartMapAPITest.cs rename to tests/CoreTests/CSharpInteroperabilityTests/APITest/ChartMapAPITest.cs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/APITest/ChartPolarAPITest.cs b/tests/CoreTests/CSharpInteroperabilityTests/APITest/ChartPolarAPITest.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/APITest/ChartPolarAPITest.cs rename to tests/CoreTests/CSharpInteroperabilityTests/APITest/ChartPolarAPITest.cs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/Plotly.NET.Tests.CSharpInteroperability.csproj b/tests/CoreTests/CSharpInteroperabilityTests/CSharpInteroperabilityTests.csproj similarity index 91% rename from tests/Plotly.NET.Tests.CSharpInteroperability/Plotly.NET.Tests.CSharpInteroperability.csproj rename to tests/CoreTests/CSharpInteroperabilityTests/CSharpInteroperabilityTests.csproj index 464f00cc3..04387c08b 100644 --- a/tests/Plotly.NET.Tests.CSharpInteroperability/Plotly.NET.Tests.CSharpInteroperability.csproj +++ b/tests/CoreTests/CSharpInteroperabilityTests/CSharpInteroperabilityTests.csproj @@ -19,7 +19,7 @@ - + diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/LayoutObjectTests.cs b/tests/CoreTests/CSharpInteroperabilityTests/LayoutObjectTests.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/LayoutObjectTests.cs rename to tests/CoreTests/CSharpInteroperabilityTests/LayoutObjectTests.cs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/StyleParamTests.cs b/tests/CoreTests/CSharpInteroperabilityTests/StyleParamTests.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/StyleParamTests.cs rename to tests/CoreTests/CSharpInteroperabilityTests/StyleParamTests.cs diff --git a/tests/Plotly.NET.Tests.CSharpInteroperability/TraceObjectTests.cs b/tests/CoreTests/CSharpInteroperabilityTests/TraceObjectTests.cs similarity index 100% rename from tests/Plotly.NET.Tests.CSharpInteroperability/TraceObjectTests.cs rename to tests/CoreTests/CSharpInteroperabilityTests/TraceObjectTests.cs diff --git a/tests/Plotly.NET.Tests/ChartAPIs/Combine.fs b/tests/CoreTests/CoreTests/ChartAPIs/Combine.fs similarity index 100% rename from tests/Plotly.NET.Tests/ChartAPIs/Combine.fs rename to tests/CoreTests/CoreTests/ChartAPIs/Combine.fs diff --git a/tests/Plotly.NET.Tests/ChartAPIs/WithAxis.fs b/tests/CoreTests/CoreTests/ChartAPIs/WithAxis.fs similarity index 100% rename from tests/Plotly.NET.Tests/ChartAPIs/WithAxis.fs rename to tests/CoreTests/CoreTests/ChartAPIs/WithAxis.fs diff --git a/tests/Plotly.NET.Tests/CommonAbstractions/Colors.fs b/tests/CoreTests/CoreTests/CommonAbstractions/Colors.fs similarity index 100% rename from tests/Plotly.NET.Tests/CommonAbstractions/Colors.fs rename to tests/CoreTests/CoreTests/CommonAbstractions/Colors.fs diff --git a/tests/Plotly.NET.Tests/CommonAbstractions/StyleParams.fs b/tests/CoreTests/CoreTests/CommonAbstractions/StyleParams.fs similarity index 100% rename from tests/Plotly.NET.Tests/CommonAbstractions/StyleParams.fs rename to tests/CoreTests/CoreTests/CommonAbstractions/StyleParams.fs diff --git a/tests/Plotly.NET.Tests/ConfigObjects/Config.fs b/tests/CoreTests/CoreTests/ConfigObjects/Config.fs similarity index 100% rename from tests/Plotly.NET.Tests/ConfigObjects/Config.fs rename to tests/CoreTests/CoreTests/ConfigObjects/Config.fs diff --git a/tests/Plotly.NET.Tests/Plotly.NET.Tests.fsproj b/tests/CoreTests/CoreTests/CoreTests.fsproj similarity index 54% rename from tests/Plotly.NET.Tests/Plotly.NET.Tests.fsproj rename to tests/CoreTests/CoreTests/CoreTests.fsproj index d31d76d92..b93817b75 100644 --- a/tests/Plotly.NET.Tests/Plotly.NET.Tests.fsproj +++ b/tests/CoreTests/CoreTests/CoreTests.fsproj @@ -8,9 +8,21 @@ - - - + + + + + + + + + + + + + + + @@ -22,20 +34,6 @@ - - - - - - - - - - - - - - @@ -48,6 +46,7 @@ - + + diff --git a/tests/Plotly.NET.Tests/DisplayOptions/DisplayOptions.fs b/tests/CoreTests/CoreTests/DisplayOptions/DisplayOptions.fs similarity index 100% rename from tests/Plotly.NET.Tests/DisplayOptions/DisplayOptions.fs rename to tests/CoreTests/CoreTests/DisplayOptions/DisplayOptions.fs diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/Chart2D.fs b/tests/CoreTests/CoreTests/HTMLCodegen/Chart2D.fs new file mode 100644 index 000000000..e7c460e55 --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/Chart2D.fs @@ -0,0 +1,532 @@ +module CoreTests.HTMLCodegen.Chart2D + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open Chart2DTestCharts + +module Scatter = + [] + let ``Scatter chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Scatter" [ + ] + ] + +module Point = + [] + let ``Point chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Point" [ + testCase "Text label data" ( fun () -> + """var data = [{"type":"scatter","name":"points","mode":"markers+text","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"text":["a","b","c","d","e","f","g","h","i","j"],"textposition":"top right","marker":{},"line":{}}];""" + |> chartGeneratedContains Point.``Point chart with text labels`` + ) + testCase "Text label layout" ( fun () -> + emptyLayout Point.``Point chart with text labels`` + ) + ] + ] + +module Line = + [] + let ``Line chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Line" [ + testCase "With LineStyle data" ( fun () -> + """var data = [{"type":"scatter","name":"line","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{"symbol":"1"},"line":{"width":2.0,"dash":"dot"}}];""" + |> chartGeneratedContains Line.``Line chart with line styling`` + ) + testCase "With LineStyle layout" ( fun () -> + emptyLayout Line.``Line chart with line styling`` + ) + testCase "Chart line data" ( fun () -> + """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0,65.0,66.0,67.0,68.0,69.0,70.0,71.0,72.0,73.0,74.0,75.0,76.0,77.0,78.0,79.0,80.0,81.0,82.0,83.0,84.0,85.0,86.0,87.0,88.0,89.0,90.0,91.0,92.0,93.0,94.0,95.0,96.0,97.0,98.0,99.0,100.0],"y":[1.0,4.0,9.0,16.0,25.0,36.0,49.0,64.0,81.0,100.0,121.0,144.0,169.0,196.0,225.0,256.0,289.0,324.0,361.0,400.0,441.0,484.0,529.0,576.0,625.0,676.0,729.0,784.0,841.0,900.0,961.0,1024.0,1089.0,1156.0,1225.0,1296.0,1369.0,1444.0,1521.0,1600.0,1681.0,1764.0,1849.0,1936.0,2025.0,2116.0,2209.0,2304.0,2401.0,2500.0,2601.0,2704.0,2809.0,2916.0,3025.0,3136.0,3249.0,3364.0,3481.0,3600.0,3721.0,3844.0,3969.0,4096.0,4225.0,4356.0,4489.0,4624.0,4761.0,4900.0,5041.0,5184.0,5329.0,5476.0,5625.0,5776.0,5929.0,6084.0,6241.0,6400.0,6561.0,6724.0,6889.0,7056.0,7225.0,7396.0,7569.0,7744.0,7921.0,8100.0,8281.0,8464.0,8649.0,8836.0,9025.0,9216.0,9409.0,9604.0,9801.0,10000.0],"marker":{},"line":{}}];""" + |> chartGeneratedContains Line.``Simple line chart`` + ) + testCase "Chart line layout" ( fun () -> + emptyLayout Line.``Simple line chart`` + ) + ] + ] + +module Spline = + [] + let ``Spline chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Spline" [ + testCase "Spline chart data" ( fun () -> + """var data = [{"type":"scatter","name":"spline","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{},"line":{"shape":"spline"}}];""" + |> chartGeneratedContains Spline.``Simple spline chart`` + ) + testCase "Spline chart layout" ( fun () -> + emptyLayout Spline.``Simple spline chart`` + ) + ] + ] + +module Bubble = + [] + let ``Bubble chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Bubble" [ + testCase "Bubble data" ( fun () -> + """var data = [{"type":"scatter","mode":"markers","x":[2,4,6],"y":[4,1,6],"marker":{"size":[19,26,55]},"line":{}}];""" + |> chartGeneratedContains Bubble.``Simple bubble chart`` + ); + testCase "Bubble layout" ( fun () -> + emptyLayout Bubble.``Simple bubble chart`` + ); + ] + ] + +module Range = + [] + let ``Range chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Range" [ + testCase "Range plot data" ( fun () -> + """var data = [{"type":"scatter","name":"lower","showlegend":true,"legendgroup":"Range","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[1.0244410755226578,1.1291130737537114,4.930085632917511,1.4292117752736488,2.5179894182449156,2.3470285278032668,1.5358344954605374,1.4046562835130172,2.6874669190437843,0.7493837949584163],"fillcolor":"lightblue","marker":{"color":"lightblue"}},{"type":"scatter","name":"upper","showlegend":true,"legendgroup":"Range","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.338369840913624,1.7844184475412679,5.2629626417825754,2.125375844363764,3.4634618528482792,3.4283738280312965,2.6463105539541276,2.4505998873853123,4.096133255211699,1.1174599459010455],"fill":"tonexty","fillcolor":"lightblue","marker":{"color":"lightblue"}},{"type":"scatter","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{"color":"grey"},"line":{},"legendgroup":"Range","legendgrouptitle":{"text":"Range"}}];""" + |> chartGeneratedContains Range.``Styled range chart`` + ); + testCase "Range plot layout" ( fun () -> + emptyLayout Range.``Styled range chart`` + ); + ] + ] + +module Area = + [] + let ``Area chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Area" [ + testCase "Simple area data" ( fun () -> + """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,2.5,5.0,7.5,5.0,2.5,7.5,4.5,5.5,5.0],"marker":{},"line":{},"fill":"tozeroy","fillpattern":{}}];""" + |> chartGeneratedContains Area.``Simple area chart`` + ); + testCase "Simple area layout" ( fun () -> + emptyLayout Area.``Simple area chart`` + ); + ] + ] + +module SplineArea = + [] + let ``SplineArea chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "SplineArea" [ + testCase "SplineArea data" ( fun () -> + """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,2.5,5.0,7.5,5.0,2.5,7.5,4.5,5.5,5.0],"marker":{},"line":{"shape":"spline"},"fill":"tozeroy","fillpattern":{}}];""" + |> chartGeneratedContains SplineArea.``Simple spline area chart`` + ); + testCase "SplineArea layout" ( fun () -> + emptyLayout SplineArea.``Simple spline area chart`` + ); + ] + ] + +module StackedArea = + [] + let ``StackedArea chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "StackedArea" [ + testCase "Stacked area data" ( fun () -> + """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,2.5,5.0,7.5,5.0,2.5,7.5,4.5,5.5,5.0],"stackgroup":"stackedarea","marker":{},"line":{},"fill":"tonexty","fillpattern":{}},{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,5.5,4.5,7.5,2.5,5.0,7.5,5.0,2.5,5.0],"stackgroup":"stackedarea","marker":{},"line":{},"fill":"tonexty","fillpattern":{}}];""" + |> chartGeneratedContains StackedArea.``Two stacked areas chart`` + ); + testCase "Stacked area layout" ( fun () -> + emptyLayout StackedArea.``Two stacked areas chart`` + ); + ] + ] + +module Funnel = + [] + let ``Funnel chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Funnel" [ + testCase "Funnel data" ( fun () -> + """var data = [{"type":"funnel","x":[1200.0,909.4,600.6,300.0,80.0],"y":["Sales person A","Sales person B","Sales person C","Sales person D","Sales person E"],"marker":{"color":"59D4E8","line":{"color":"3E4E88","width":2.0}},"connector":{"line":{"color":"royalblue","width":3.0,"dash":"dot"}}}];""" + |> chartGeneratedContains Funnel.``Simple funnel chart`` + ); + testCase "Funnel layout" ( fun () -> + "var layout = {\"margin\":{\"l\":100}};" + |> chartGeneratedContains Funnel.``Simple funnel chart`` + ); + ] + ] + +module StackedFunnel = + [] + let ``StackedFunnel chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "StackedFunnel" [ + ] + ] + +module Waterfall = + [] + let ``Waterfall chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Waterfall" [ + ] + ] + +module Bar = + [] + let ``Bar chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Bar" [ + testCase "Bar chart data" ( fun () -> + """var data = [{"type":"bar","x":[20,14,23],"y":["Product A","Product B","Product C"],"orientation":"h","marker":{"pattern":{}}}];""" + |> chartGeneratedContains Bar.``Simple bar chart`` + ); + testCase "Bar chart layout" ( fun () -> + emptyLayout Bar.``Simple bar chart`` + ); + ] + ] + +module StackedBar = + [] + let ``StackedBar chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "StackedBar" [ + testCase "Stacked bar data" ( fun () -> + """var data = [{"type":"bar","name":"old","x":[20,14,23],"y":["Product A","Product B","Product C"],"orientation":"h","marker":{"pattern":{}}},{"type":"bar","name":"new","x":[8,21,13],"y":["Product A","Product B","Product C"],"orientation":"h","marker":{"pattern":{}}}];""" + |> chartGeneratedContains Bar.``Two stacked bars chart`` + ); + testCase "Stacked bar layout" ( fun () -> + "var layout = {\"barmode\":\"stack\"};" + |> chartGeneratedContains Bar.``Two stacked bars chart`` + ); + ] + ] + +module Column = + [] + let ``Column chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Column" [ + testCase "Column chart data" ( fun () -> + """var data = [{"type":"bar","x":["Product A","Product B","Product C"],"y":[20,14,23],"orientation":"v","marker":{"pattern":{}}}];""" + |> chartGeneratedContains Column.``Simple column chart`` + ); + testCase "Column chart layout" ( fun () -> + emptyLayout Column.``Simple column chart`` + ); + ] + ] + +module StackedColumn = + [] + let ``StackedColumn chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "StackedColumn" [ + testCase "Stacked column data" ( fun () -> + """var data = [{"type":"bar","name":"old","x":["Product A","Product B","Product C"],"y":[20,14,23],"orientation":"v","marker":{"pattern":{}}},{"type":"bar","name":"new","x":["Product A","Product B","Product C"],"y":[8,21,13],"orientation":"v","marker":{"pattern":{}}}];""" + |> chartGeneratedContains Column.``Two stacked columns chart`` + ); + testCase "Stacked column layout" ( fun () -> + "var layout = {\"barmode\":\"stack\"};" + |> chartGeneratedContains Column.``Two stacked columns chart`` + ); + ] + ] + +module Histogram = + [] + let ``Histogram chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Histogram" [ + testCase "Histo data" ( fun () -> + // the string is too big to be here fully. + [ + "var data = [{\"type\":\"histogram\",\"x\":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985" + """0.7608672612164483,0.8280196519699039,0.040246858280267035,0.0017312127173557937],"marker":{"pattern":{}}}];""" + ] + |> chartGeneratedContainsList Histogram.``Simple histogram`` + ); + testCase "Histo layout" ( fun () -> + "var layout = {\"width\":500,\"height\":500};" + |> chartGeneratedContains Histogram.``Simple histogram`` + ); + ] + ] + +module Histogram2D = + [] + let ``Histogram2D chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Histogram2D" [ + testCase "Histo 2D data" ( fun () -> + "var data = [{\"type\":\"histogram2d\",\"x\":[-1.566002360265054,-1.833996340961623,-1.0330391275776571,-0.8476993487909306,-0.8471270832604864,-1.021055309868153,-0.5368298779218124,-0.9982579324563884,-0.6367576994858231,-1.433590036163408,-1.3735531103452598" + |> chartGeneratedContains Histogram2D.``Histogram2D of 2D normal distribution`` + ); + testCase "Histo 2D layout" ( fun () -> + emptyLayout Histogram2D.``Histogram2D of 2D normal distribution`` + ); + ] + ] + +module BoxPlot = + [] + let ``BoxPlot chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "BoxPlot" [ + testCase "Box1 data" ( fun () -> + """var data = [{"type":"box","x":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{},"line":{},"boxpoints":"all","jitter":0.1}];""" + |> chartGeneratedContains BoxPlot.``Simple boxplot with boxpoints`` + ); + testCase "Box1 layout" ( fun () -> + emptyLayout BoxPlot.``Simple boxplot with boxpoints`` + ); + testCase "Box2 data" ( fun () -> + """var data = [{"type":"box","x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"y":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"orientation":"h","marker":{},"line":{},"boxpoints":"all","jitter":0.1}];""" + |> chartGeneratedContains BoxPlot.``Simple horizontal boxplot with boxpoints`` + ); + testCase "Box2 layout" ( fun () -> + emptyLayout BoxPlot.``Simple horizontal boxplot with boxpoints`` + ); + testCase "Box3 data" ( fun () -> + """var data = [{"type":"box","name":"bin1","marker":{},"line":{},"boxpoints":"all","jitter":0.1,"x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]},{"type":"box","name":"bin2","marker":{},"line":{},"boxpoints":"all","jitter":0.1,"x":[2.0,1.5,5.0,1.5,2.0,2.5,2.1,2.5,1.5,1.0,2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]}];""" + |> chartGeneratedContains BoxPlot.``Combined chart with 2 horizontal boxplots`` + ); + testCase "Box3 layout" ( fun () -> + emptyLayout BoxPlot.``Combined chart with 2 horizontal boxplots`` + ); + ] + ] + +module Violin = + [] + let ``Violin chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Violin" [ + testCase "Violin1 data" ( fun () -> + """var data = [{"type":"violin","x":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{},"line":{},"box":{},"points":"all"}];""" + |> chartGeneratedContains Violin.``Simple violin plot with jitterpoints`` + ); + testCase "Violin1 layout" ( fun () -> + emptyLayout Violin.``Simple violin plot with jitterpoints`` + ); + testCase "Violin2 data" ( fun () -> + """var data = [{"type":"violin","x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"y":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"orientation":"h","marker":{},"line":{},"box":{},"jitter":0.1,"meanline":{"visible":true},"points":"all"}];""" + |> chartGeneratedContains Violin.``Simple horizontal violin plot with jitterpoints`` + ); + testCase "Violin2 layout" ( fun () -> + emptyLayout Violin.``Simple horizontal violin plot with jitterpoints`` + ); + testCase "Violin3 data" ( fun () -> + """var data = [{"type":"violin","name":"bin1","marker":{},"line":{},"box":{},"jitter":0.1,"points":"all","x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]},{"type":"violin","name":"bin2","marker":{},"line":{},"box":{},"jitter":0.1,"points":"all","x":[2.0,1.5,5.0,1.5,2.0,2.5,2.1,2.5,1.5,1.0,2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]}];""" + |> chartGeneratedContains Violin.``Combined chart with 2 horizontal violin plots`` + ); + testCase "Violin3 layout" ( fun () -> + emptyLayout Violin.``Combined chart with 2 horizontal violin plots`` + ); + ] + ] + +module Histogram2DContour = + [] + let ``Histogram2DContour chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Histogram2DContour" [ + testCase "Histo contour data" ( fun () -> + "var data = [{\"type\":\"histogram2dcontour\",\"x\":[-1.566002360265054,-1.833996340961623,-1.0330391275776571,-0.8476993487909306,-0.8471270832604864,-1.021055309868153,-0.5368298779218124,-0.9982579324563884,-0.6367576994858231,-1.433590036163408,-1.3735531103452598" + |> chartGeneratedContains Histogram2DContour.``Histogram2DContour of 2D normal distribution`` + ); + testCase "Histo contour layout" ( fun () -> + emptyLayout Histogram2DContour.``Histogram2DContour of 2D normal distribution`` + ); + ] + ] + +module Heatmap = + [] + let ``Heatmap chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Heatmap" [ + testCase "Heatmap data" ( fun () -> + """var data = [{"type":"heatmap","x":["Tp0","Tp30","Tp60","Tp160"],"y":["p3","p2","p1"],"z":[[1.0,1.5,0.7,2.7],[2.0,0.5,1.2,1.4],[0.1,2.6,2.4,3.0]],"colorscale":[[0.0,"#3D9970"],[1.0,"#001f3f"]],"showscale":true}];""" + |> chartGeneratedContains Heatmap.``simple heatmap with custom colorscale`` + ); + testCase "Heatmap layout" ( fun () -> + "var layout = {\"width\":700,\"height\":500,\"margin\":{\"l\":200.0}};" + |> chartGeneratedContains Heatmap.``simple heatmap with custom colorscale`` + ); + testCase "Heatmap styled data" ( fun () -> + """var data = [{"type":"heatmap","x":["Tp0","Tp30","Tp60","Tp160"],"y":["p3","p2","p1"],"z":[[1.0,1.5,0.7,2.7],[2.0,0.5,1.2,1.4],[0.1,2.6,2.4,3.0]],"colorscale":[[0.0,"#3D9970"],[1.0,"#001f3f"]],"showscale":true,"colorbar":{"title":{"text":"Im the Colorbar"}}}];""" + |> chartGeneratedContains Heatmap.``Simple heatmap with custom colorscale and styled colorbar`` + ); + testCase "Heatmap styled layout" ( fun () -> + "var layout = {\"width\":700,\"height\":500,\"margin\":{\"l\":200.0}};" + |> chartGeneratedContains Heatmap.``Simple heatmap with custom colorscale and styled colorbar`` + ); + ] + ] + +module AnnotatedHeatmap = + [] + let ``AnnotatedHeatmap chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "AnnotatedHeatmap" [ + testCase "Annotated heatmap data" ( fun () -> + """var data = [{"type":"heatmap","x":["C1","C2","C3"],"y":["R1","R2","R3"],"z":[[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]}];""" + |> chartGeneratedContains AnnotatedHeatmap.``Simple annotated heatmap`` + ); + testCase "Annotated heatmap layout" ( fun () -> + """var layout = {"yaxis":{"autorange":"reversed"},"annotations":[{"x":0,"y":0,"showarrow":false,"text":"1,1"},{"x":1,"y":0,"showarrow":false,"text":"1,2"},{"x":2,"y":0,"showarrow":false,"text":"1,3"},{"x":0,"y":1,"showarrow":false,"text":"2,1"},{"x":1,"y":1,"showarrow":false,"text":"2,2"},{"x":2,"y":1,"showarrow":false,"text":"2,3"},{"x":0,"y":2,"showarrow":false,"text":"3,1"},{"x":1,"y":2,"showarrow":false,"text":"3,2"},{"x":2,"y":2,"showarrow":false,"text":"3,3"}]};""" + |> chartGeneratedContains AnnotatedHeatmap.``Simple annotated heatmap`` + ); + ] + ] + +module Image = + [] + let ``Image chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Image" [ + testCase "Image raw data" ( fun () -> + """var data = [{"type":"image","z":[[[0,0,255],[255,255,0],[0,0,255]],[[255,0,0],[255,0,255],[255,0,255]],[[0,255,0],[0,255,255],[255,0,0]]]}];""" + |> chartGeneratedContains Image.``Raw color component image chart`` + ); + testCase "Image raw layout" ( fun () -> + """var layout = {"title":{"text":"Image chart from raw color component arrays"}};""" + |> chartGeneratedContains Image.``Raw color component image chart`` + ); + + testCase "Image raw hsl data" ( fun () -> + """var data = [{"type":"image","z":[[[0,0,255],[255,255,0],[0,0,255]],[[255,0,0],[255,0,255],[255,0,255]],[[0,255,0],[0,255,255],[255,0,0]]],"colormodel":"hsl"}];""" + |> chartGeneratedContains Image.``HSL image chart`` + ); + testCase "Image raw hsl layout" ( fun () -> + """var layout = {"title":{"text":"HSL color model"}};""" + |> chartGeneratedContains Image.``HSL image chart`` + ); + + testCase "Image ARGB data" ( fun () -> + """var data = [{"type":"image","z":[[[240,248,255,255],[255,248,220,255],[255,240,245,255]],[[169,169,169,255],[255,250,250,255],[25,25,112,255]],[[176,196,222,255],[189,183,107,255],[135,206,250,255]]],"colormodel":"rgba"}];""" + |> chartGeneratedContains Image.``ARGB image chart`` + ); + testCase "Image ARGB layout" ( fun () -> + """var layout = {"title":{"text":"ARGB image chart"}};""" + |> chartGeneratedContains Image.``ARGB image chart`` + ); + + testCase "Image base64 data" ( fun () -> + """var data = [{"type":"image","source":"data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAABlEAAAZRCAYAAAAh6rVPAAABgGlDQ1BzUkdCIElFQzYxOTY2LTIuMQAAKJF1kc8rRFEUxz9maOTXKBYWFpOwQoya2FiMGAqLMcqvzcz13oyaH6/3RpKtslWU2Pi14C9gq6yVIlKyU9bEBj3neWommXO7537u955zuvdc8MTSKmOVd0MmmzejkXBgemY24HuijDp81OOPK8sYnxyOUdLebyVa7LrTqVU67l+rXtAsBWWVwgPKMPPCI8Jjy3nD4S3hRpWKLwifCHeYckHhG0dPuPzscNLlT4fNWHQQPPXCgWQRJ4pYpcyMsLyc1kx6Sf3ex3lJjZadmpS1RWYzFlEihAkwyhCDhOihX3yIToJ0yY4S+d0/+RPkJFeJN1jBZJEkKfJ0iLok1TVZddE1GWlWnP7/7aul9wbd6jVhqHi07dc28G3C14ZtfxzY9tcheB/gPFvIz+1D35voGwWtdQ/8a3B6UdAS23C2Dk33RtyM/0hemR5dh5djqJ2BhiuomnN79nvO0R3EVuWrLmFnF9ol3j//DQZdZ7qcwvX+AAAACXBIWXMAAC4jAAAuIwF4pT92AAAgAElEQVR4nOzZO66t2VmF4b8sC1GZC8hdjXAD6AMJoSU3gEqqFU6wZDkrqUIiGoCTagrkSA6dIBXJ3sfnsi9rjfVfxpzzecIvGsGXvdsGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwkR+//v7vr95Av29++NmfAADARX5x9QAAAFjRj19//+22bX+8egdD+OM3P/z87dUjAABgRSIKAACc7Cmg/LRt27eXDmEU327b9pOQAgAA5xNRAADgRB8FlF9fu4TB/HoTUgAA4HQiCgAAnERA4UFCCgAAnExEAQCAEwgo7ERIAQCAE4koAABwMAGFnQkpAABwEhEFAAAOJKBwECEFAABOIKIAAMBBBBQOJqQAAMDBRBQAADiAgMJJhBQAADiQiAIAADsTUDiZkAIAAAcRUQAAYEcCChcRUgAA4AAiCgAA7ERA4WJCCgAA7ExEAQCAHQgolBBSAABgRyIKAAA8SEChjJACAAA7EVEAAOABAgqlhBQAANiBiAIAACEBhXJCCgAAPEhEAQCAgIDCIIQUAAB4gIgCAAB3ElAYjJACAAAhEQUAAO4goDAoIQUAAAIiCgAA3EhAYXBCCgAA3ElEAQCAGwgoTEJIAQCAO4goAADwDgGFyQgpAABwIxEFAADeIKAwKSEFAABuIKIAAMArBBQmJ6QAAMA7RBQAAHiBgMIihBQAAHiDiAIAAJ8RUFiMkAIAAK8QUQAA4CMCCosSUgAA4AUiCgAAPBFQWJyQAgAAnxFRAABgE1DgiZACAAAfEVEAAFiegAKfEFIAAOCJiAIAwNIEFHiRkAIAAJuIAgDAwgQUeJOQAgDA8kQUAACWJKDATYQUAACWJqIAALAcAQXuIqQAALAsEQUAgKUIKBARUgAAWJKIAgDAMgQUeIiQAgDAckQUAACWIKDALoQUAACWIqIAADA9AQV2JaQAALAMEQUAgKkJKHAIIQUAgCWIKAAATEtAgUMJKQAATE9EAQBgSgIKnEJIAQBgaiIKAADTEVDgVEIKAADTElEAAJiKgAKXEFIAAJiSiAIAwDQEFLiUkAIAwHREFAAApiCgQAUhBQCAqYgoAAAMT0CBKkIKAADTEFEAABiagAKVhBQAAKYgogAAMCwBBaoJKQAADE9EAQBgSAIKDEFIAQBgaCIKAADDEVBgKEIKAADDElEAABiKgAJDElIAABiSiAIAwDAEFBiakAIAwHBEFAAAhiCgwBSEFAAAhiKiAABQT0CBqQgpAAAMQ0QBAKCagAJTElIAABiCiAIAQC0BBaYmpAAAUE9EAQCgkoACSxBSAACoJqIAAFBHQIGlCCkAANQSUQAAqCKgwJKEFAAAKokoAADUEFBgaUIKAAB1RBQAACoIKMAmpAAAUEZEAQDgcgIK8BEhBQCAGiIKAACXElCAFwgpAABUEFEAALiMgAK8QUgBAOByIgoAAJcQUIAbCCkAAFxKRAEA4HQCCnAHIQUAgMuIKAAAnEpAAQJCCgAAlxBRAAA4jYACPEBIAQDgdCIKAACnEFCAHQgpAACcSkQBAOBwAgqwIyEFAIDTiCgAABxKQAEOIKQAAHAKEQUAgMMIKMCBhBQAAA4nogAAcAgBBTiBkAIAwKFEFAAAdiegACcSUgAAOIyIAgDArgQU4AJCCgAAhxBRAADYjYACXEhIAQBgdyIKAAC7EFCAAkIKAAC7ElEAAHiYgAIUEVIAANiNiAIAwEMEFKCQkAIAwC5EFAAAYgIKUExIAQDgYSIKAAARAQUYgJACAMBDRBQAAO4moAADEVIAAIiJKAAA3EVAAQYkpAAAEBFRAAC4mYACDExIAQDgbiIKAAA3EVCACQgpAADcRUQBAOBdAgowESEFAICbiSgAALxJQAEmJKQAAHATEQUAgFcJKMDEhBQAAN4logAA8CIBBViAkAIAwJtEFAAAviCgAAsRUgAAeJWIAgDAJwQUYEFCCgAALxJRAAD4QEABFiakAADwBREFAIBt2wQUgE1IAQDgMyIKAAACCsDfCCkAAHwgogAALE5AAfiCkAIAwLZtIgoAwNIEFIBXCSkAAIgoAACrElAA3iWkAAAsTkQBAFiQgAJwMyEFAGBhIgoAwGIEFIC7CSkAAIsSUQAAFiKgAMSEFACABYkoAACLEFAAHiakAAAsRkQBAFiAgAKwGyEFAGAhIgoAwOQEFIDdCSkAAIsQUQAAJiagABxGSAEAWICIAgAwKQEF4HBCCgDA5EQUAIAJCSgApxFSAAAmJqIAAExGQAE4nZACADApEQUAYCICCsBlhBQAgAmJKAAAkxBQAC4npAAATEZEAQCYgIACUENIAQCYiIgCADA4AQWgjpACADAJEQUAYGACCkAtIQUAYAIiCgDAoAQUgHpCCgDA4EQUAIABCSgAwxBSAAAGJqIAAAxGQAEYjpACADAoEQUAYCACCsCwhBQAgAGJKAAAgxBQAIYnpAAADEZEAQAYgIACMA0hBQBgICIKAEA5AQVgOkIKAMAgRBQAgGICCsC0hBQAgAGIKAAApQQUgOkJKQAA5UQUAIBCAgrAMoQUAIBiIgoAQBkBBWA5QgoAQCkRBQCgiIACsCwhBQCgkIgCAFBCQAFYnpACAFBGRAEAKCCgAPBESAEAKCKiAABcTEAB4DNCCgBACREFAOBCAgoArxBSAAAKiCgAABcRUAB4h5ACAHAxEQUA4AICCgA3ElIAAC4kogAAnExAAeBOQgoAwEVEFACAEwkoAISEFACAC4goAAAnEVAAeJCQAgBwMhEFAOAEAgoAOxFSAABOJKIAABxMQAFgZ0IKAMBJRBQAgAMJKAAcREgBADiBiAIAcBABBYCDCSkAAAcTUQAADiCgAHASIQUA4EAiCgDAzgQUAE4mpAAAHEREAQDYkYACwEWEFACAA4goAAA7EVAAuJiQAgCwMxEFAGAHAgoAJYQUAIAdiSgAAA8SUAAoI6QAAOxERAEAeICAAkApIQUAYAciCgBASEABoJyQAgDwIBEFACAgoAAwCCEFAOABIgoAwJ0EFAAGI6QAAIREFACAOwgoAAxKSAEACIgoAAA3ElAAGJyQAgBwJxEFAOAGAgoAkxBSAADuIKIAALxDQAFgMkIKAMCNRBQAgDcIKABMSkgBALiBiAIA8AoBBYDJCSkAAO8QUQAAXiCgALAIIQUA4A0iCgDAZwQUABYjpAAAvEJEAQD4iIACwKKEFACAF4goAABPBBQAFiekAAB8RkQBANgEFAB4IqQAAHxERAEAliegAMAnhBQAgCciCgCwNAEFAF4kpAAAbCIKALAwAQUA3iSkAADLE1EAgCUJKABwEyEFAFiaiAIALEdAAYC7CCkAwLJEFABgKQIKAESEFABgSSIKALAMAQUAHiKkAADLEVEAgCUIKACwCyEFAFiKiAIATE9AAYBdCSkAwDJEFABgagIKABxCSAEAliCiAADTElAA4FBCCgAwPREFAJiSgAIApxBSAICpiSgAwHQEFAA4lZACAExLRAEApiKgAMAlhBQAYEoiCgAwDQEFAC4lpAAA0xFRAIApCCgAUEFIAQCmIqIAAMMTUACgipACAExDRAEAhiagAEAlIQUAmIKIAgAMS0ABgGpCCgAwPBEFABiSgAIAQxBSAIChiSgAwHAEFAAYipACAAxLRAEAhiKgAMCQhBQAYEgiCgAwDAEFAIYmpAAAwxFRAIAhCCgAMAUhBQAYiogCANQTUABgKkIKADAMEQUAqCagAMCUhBQAYAgiCgBQS0ABgKkJKQBAPREFAKgkoADAEoQUAKCaiAIA1BFQAGApQgoAUEtEAQCqCCgAsCQhBQCoJKIAADUEFABYmpACANQRUQCACgIKALAJKQBAGREFALicgAIAfERIAQBqiCgAwKUEFADgBUIKAFBBRAEALiOgAABvEFIAgMuJKADAJQQUAOAGQgoAcCkRBQA4nYACANxBSAEALiOiAACnElAAgICQAgBcQkQBAE4joAAADxBSAIDTiSgAwCkEFABgB0IKAHAqEQUAOJyAAgDsSEgBAE4jogAAhxJQAIADCCkAwClEFADgMAIKAHAgIQUAOJyIAgAcQkABAE4gpAAAhxJRAIDdCSgAwImEFADgMCIKALArAQUAuICQAgAcQkQBAHYjoAAAFxJSAIDdiSgAwC4EFACggJACAOxKRAEAHiagAABFhBQAYDciCgDwEAEFACgkpAAAuxBRAICYgAIAFBNSAICHiSgAQERAAQAGIKQAAA8RUQCAuwkoAMBAhBQAICaiAAB3EVAAgAEJKQBAREQBAG4moAAAAxNSAIC7iSgAwE0EFABgAkIKAHAXEQUAeJeAAgBMREgBAG4mogAAbxJQAIAJCSkAwE1EFADgVQIKADAxIQUAeJeIAgC8SEABABYgpAAAbxJRAIAvCCgAwEKEFADgVSIKAPAJAQUAWJCQAgC8SEQBAD4QUACAhQkpAMAXRBQAYNs2AQUAYBNSAIDPiCgAgIACAPA3QgoA8IGIAgCLE1AAAL4gpAAA27aJKACwNAEFAOBVQgoAIKIAwKoEFACAdwkpALA4EQUAFiSgAADcTEgBgIWJKACwGAEFAOBuQgoALEpEAYCFCCgAADEhBQAWJKIAwCIEFACAhwkpALAYEQUAFiCgAADsRkgBgIWIKAAwOQEFAGB3QgoALEJEAYCJCSgAAIcRUgBgASIKAExKQAEAOJyQAgCTE1EAYEICCgDAaYQUAJiYiAIAkxFQAABOJ6QAwKREFACYiIACAHAZIQUAJiSiAMAkBBQAgMsJKQAwGREFACYgoAAA1BBSAGAiIgoADE5AAQCoI6QAwCREFAAYmIACAFBLSAGACYgoADAoAQUAoJ6QAgCDE1EAYEACCgDAMIQUABiYiAIAgxFQAACGI6QAwKBEFAAYiIACADAsIQUABiSiAMAgBBQAgOEJKQAwGBEFAAYgoAAATENIAYCBiCgAUE5AAQCYjpACAIMQUQCgmIACADAtIQUABiCiAEApAQUAYHpCCgCUE1EAoJCAAgCwDCEFAIqJKABQRkABAFiOkAIApUQUACgioAAALEtIAYBCIgoAlBBQAACWJ6QAQBkRBQAKCCgAADwRUgCgiIgCABcTUAAA+IyQAgAlRBQAuJCAAgDAK4QUACggogDARQQUAADeIaQAwMVEFAC4gIACAMCNhBQAuJCIAgAnE1AAALiTkAIAFxFRAOBEAgoAACEhBQAuIKIAwEkEFAAAHiSkAMDJRBQAOIGAAgDAToQUADiRiAIABxNQAADYmZACACcRUQDgQAIKAAAHEVIA4AQiCgAcREABAOBgQgoAHExEAYADCCgAAJxESAGAA4koALAzAQUAgJMJKQBwEBEFAHYkoAAAcBEhBQAOIKIAwE4EFAAALiakAMDORBQA2IGAAgBACSEFAHYkogDAgwQUAADKCCkAsBMRBQAeIKAAAFBKSAGAHYgoABASUAAAKCekAMCDRBQACAgoAAAMQkgBgAeIKABwJwEFAIDBCCkAEBJRAOAOAgoAAIMSUgAgIKIAwI0EFAAABiekAMCdRBQAuIGAAgDAJIQUALiDiAIA7xBQAACYjJACADcSUQDgDQIKAACTElIA4AYiCgC8QkABAGByQgoAvENEAYAXCCgAACxCSAGAN4goAPAZAQUAgMUIKQDwChEFAD4ioAAAsCghBQBeIKIAwBMBBQCAxQkpAPAZEQUANgEFAACeCCkA8BERBYDlCSgAAPAJIQUAnogoACxNQAEAgBcJKQCwiSgALExAAQCANwkpACxPRAFgSQIKAADcREgBYGkiCgDLEVCAIv959QCG4E+AqwkpACxLRAFgKQIKUOS73/7193+6egT9/vK7r/60bdt3V+8AliekALCkr64eAABnEVCAIt/99q+//8PVIxjLNz/8/G/btv371TuA5f3Ptm3//JffffXfVw8BgDOIKAAsQUABiggoxIQUoISQAsAyRBQApiegAEUEFB4mpAAlhBQAliCiADA1AQUoIqCwGyEFKCGkADA9EQWAaQkoQBEBhd0JKUAJIQWAqYkoAExJQAGKCCgcRkgBSggpAExLRAFgOgIKUERA4XBCClBCSAFgSr+4egAA7ElAAYoIKIF//Zf/+rurN4zmL7/76g/btn139Q5geb/etu2nb374+durhwDAnkQUAKYhoABFBJTAj19//5v//ad//I9vfvj5N1dvGY2QApQQUgCYjogCwBQEFKCIgBL48evvf7Nt25//75e//Idt2/4spNxPSAFKCCkATEVEAWB4AgpQREAJPAeUbdt+9XT61SakRIQUoISQAsA0RBQAhiagAEUElMALAeWZkBISUoASQgoAUxBRABiWgAIUEVACbwSUZ0JKSEgBSggpAAxPRAFgSAIKUERACdwQUJ4JKSEhBSghpAAwNBEFgOEIKEARASVwR0B5JqSEhBSghJACwLBEFACGIqAARQSUQBBQngkpISEFKCGkADAkEQWAYQgoQBEBJfBAQHkmpISEFKCEkALAcEQUAIYgoABFBJTADgHlmZASElKAEkIKAEMRUQCoJ6AARQSUwI4B5ZmQEhJSgBJCCgDDEFEAqCagAEUElMABAeWZkBISUoASQgoAQxBRAKgloABFBJTAgQHlmZASElKAEkIKAPVEFAAqCShAEQElcEJAeSakhIQUoISQAkA1EQWAOgIKUERACZwYUJ4JKSEhBSghpABQS0QBoIqAAhQRUAIXBJRnQkpISAFKCCkAVBJRAKghoABFBJTAhQHlmZASElKAEkIKAHVEFAAqCChAEQElUBBQngkpISEFKCGkAFBFRAHgcgIKUERACRQFlGdCSkhIAUoIKQDUEFEAuJSAAhQRUAKFAeWZkBISUoASQgoAFUQUAC4joABFBJRAcUB5JqSEhBSghJACwOVEFAAuIaAARQSUwAAB5ZmQEhJSgBJCCgCXElEAOJ2AAhQRUAIDBZRnQkpISAFKCCkAXEZEAeBUAgpQREAJDBhQngkpISEFKCGkAHAJEQWA0wgoQBEBJTBwQHkmpISEFKCEkALA6UQUAE4hoABFBJTABAHlmZASElKAEkIKAKcSUQA4nIACFBFQAhMFlGdCSkhIAUoIKQCcRkQB4FACClBEQAlMGFCeCSkhIQUoIaQAcAoRBYDDCChAEQElMHFAeSakhIQUoISQAsDhRBQADiGgAEUElMACAeWZkBISUoASQgoAhxJRANidgAIUEVACCwWUZ0JKSEgBSggpABxGRAFgVwIKUERACSwYUJ4JKSEh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"}];""" + |> chartGeneratedContains Image.``Image chart from base64 string`` + ); + testCase "Image base64 layout" ( fun () -> + """var layout = {"title":{"text":"This is Plotly.NET:"}};""" + |> chartGeneratedContains Image.``Image chart from base64 string`` + ); + ] + ] + +module Contour = + [] + let ``Contour chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Contour" [ + testCase "Contour data" ( fun () -> + "var data = [{\"type\":\"contour\",\"z\":[[0.3929110807586562,0.39272903726671055,0.3923748718856843,0.3918384347714509,0.39110921172503726,0.39017633288191317,0.38902858492457726" + |> chartGeneratedContains Contour.``Contour plot with peak and sink`` + ); + testCase "Contour layout" ( fun () -> + "var layout = {\"width\":600,\"height\":600};" + |> chartGeneratedContains Contour.``Contour plot with peak and sink`` + ); + ] + ] + +module OHLC = + [] + let ``OHLC chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "OHLC" [ + testCase "simple OHLC data" ( fun () -> + """var data = [{"type":"ohlc","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" + |> chartGeneratedContains OHLC.``Simple OHLC chart`` + ); + testCase "simple OHLC layout" ( fun () -> + """var layout = {"xaxis":{"rangeslider":{"yaxis":{}}}};""" + |> chartGeneratedContains OHLC.``Simple OHLC chart`` + ); + testCase "OHLC no range slider data" ( fun () -> + """var data = [{"type":"ohlc","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" + |> chartGeneratedContains OHLC.``Simple OHLC chart without range slider`` + ); + testCase "OHLC no range slider layout" ( fun () -> + """var layout = {"xaxis":{"rangeslider":{"visible":false,"yaxis":{}}}};""" + |> chartGeneratedContains OHLC.``Simple OHLC chart without range slider`` + ); + ] + ] + +module Candlestick = + [] + let ``Candlestick chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Candlestick" [ + testCase "simple Candlestick data" ( fun () -> + """var data = [{"type":"candlestick","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" + |> chartGeneratedContains Candlestick.``Simple candlestick chart`` + ); + testCase "simple Candlestick layout" ( fun () -> + """var layout = {"xaxis":{"rangeslider":{"yaxis":{}}}};""" + |> chartGeneratedContains Candlestick.``Simple candlestick chart`` + ); + testCase "Candlestick no range slider data" ( fun () -> + """var data = [{"type":"candlestick","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" + |> chartGeneratedContains Candlestick.``Simple candlestick chart without range slider`` + ); + testCase "Candlestick no range slider layout" ( fun () -> + """var layout = {"xaxis":{"rangeslider":{"visible":false,"yaxis":{}}}};""" + |> chartGeneratedContains Candlestick.``Simple candlestick chart without range slider`` + ); + ] + ] + +module Splom = + [] + let ``Splom chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "Splom" [ + testCase "Scatterplot matrix data" ( fun () -> + """var data = [{"type":"splom","dimensions":[{"label":"A","values":[1.0,4.0,3.4,0.7],"axis":{}},{"label":"B","values":[3.0,1.5,1.7,2.3],"axis":{}},{"label":"C","values":[2.0,4.0,3.1,5.0],"axis":{}},{"label":"D","values":[4.0,2.0,2.0,4.0],"axis":{}}],"marker":{"color":"blue"},"diagonal":{}}];""" + |> chartGeneratedContains Splom.``Simple scatterplot matrix`` + ); + testCase "Scatterplot matrix layout" ( fun () -> + emptyLayout Splom.``Simple scatterplot matrix`` + ); + ] + ] + +module PointDensity = + [] + let ``PointDensity chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart2D" [ + testList "PointDensity" [ + testCase "PointDensity data" ( fun () -> + """var data = [{"type":"histogram2dcontour","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"line":{"width":0.0},"contours":{"coloring":"fill","showlines":false}},{"type":"scatter","opacity":0.3,"mode":"markers","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"marker":{}}];""" + |> chartGeneratedContains PointDensity.``Simple PointDensity chart`` + ); + testCase "PointDensity layout" ( fun () -> + emptyLayout PointDensity.``Simple PointDensity chart`` + ); + testCase "PointDensity styled data" ( fun () -> + """var data = [{"type":"histogram2dcontour","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"line":{"width":0.0},"colorbar":{"title":{"text":"Density"}},"colorscale":"Viridis","contours":{"coloring":"fill","showlabels":true,"showlines":false}},{"type":"scatter","opacity":0.3,"mode":"markers","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"marker":{"color":"rgba(128, 0, 128, 1.0)","size":4,"symbol":"4"}}];""" + |> chartGeneratedContains PointDensity.``Styled PointDensity chart`` + ); + testCase "PointDensity styled layout" ( fun () -> + emptyLayout PointDensity.``Styled PointDensity chart`` + ); + ] + ] + diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/Chart3D.fs b/tests/CoreTests/CoreTests/HTMLCodegen/Chart3D.fs new file mode 100644 index 000000000..1300c2f1e --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/Chart3D.fs @@ -0,0 +1,186 @@ +module CoreTests.HTMLCodegen.Chart3D + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open Chart3DTestCharts + +module Scatter3D = + [] + let ``Scatter3D chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Scatter3D" [ + testCase "3D Scatter charts data" ( fun () -> + """var data = [{"type":"scatter3d","mode":"markers","x":[19,26,55],"y":[19,26,55],"z":[19,26,55],"marker":{},"line":{}}];""" + |> chartGeneratedContains Scatter3D.``Simple scatter3d chart with axis titles`` + ); + testCase "3D Scatter charts layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"my x-axis"}},"yaxis":{"title":{"text":"my y-axis"}},"zaxis":{"title":{"text":"my z-axis"}}},"width":800,"height":800};""" + |> chartGeneratedContains Scatter3D.``Simple scatter3d chart with axis titles`` + ); + ] + ] + +module Point3D = + [] + let ``Point3D chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Point3D" [ + testCase "3D Point charts data" ( fun () -> + """var data = [{"type":"scatter3d","mode":"markers","x":[19,26,55],"y":[19,26,55],"z":[19,26,55],"marker":{},"line":{}}];""" + |> chartGeneratedContains Point3D.``Simple Point3D chart with axis titles`` + ); + testCase "3D Point charts layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"my x-axis"}},"yaxis":{"title":{"text":"my y-axis"}},"zaxis":{"title":{"text":"my z-axis"}}},"width":800,"height":800};""" + |> chartGeneratedContains Point3D.``Simple Point3D chart with axis titles`` + ); + ] + ] + +module Line3D = + [] + let ``Line3D chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Line3D" [ + testCase "Line data" ( fun () -> + """var data = [{"type":"scatter3d","mode":"lines+markers","x":[10.0,8.765,5.376,0.699,-4.079,-7.762,-9.458,-8.797,-6.02,-1.898,2.489,6.042,7.925,7.774,5.766,2.536,-1.014,-3.973,-5.664,-5.8,-4.534,-2.366,0.02,1.974,3.058,3.146,2.427,1.303,0.232,-0.428,-0.537],"y":[0.0,4.788,8.373,9.863,8.912,5.799,1.348,-3.295,-6.971,-8.802,-8.415,-6.015,-2.306,1.713,5.025,6.863,6.893,5.27,2.562,-0.437,-2.939,-4.377,-4.536,-3.576,-1.944,-0.209,1.124,1.76,1.684,1.127,0.46],"z":[0.0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0,10.5,11.0,11.5,12.0,12.5,13.0,13.5,14.0,14.5,15.0],"marker":{},"line":{}}];""" + |> chartGeneratedContains Line3D.``Upwards spiral line 3D chart with markers`` + ); + testCase "Line layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"x-axis"}},"yaxis":{"title":{"text":"y-axis"}},"zaxis":{"title":{"text":"z-axis"}}},"width":800,"height":800};""" + |> chartGeneratedContains Line3D.``Upwards spiral line 3D chart with markers`` + ); + ] + ] + +module Bubble3D = + [] + let ``Bubble3D chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Bubble3D" [ + testCase "Bubble data" ( fun () -> + """var data = [{"type":"scatter3d","mode":"markers+text","x":[1,6,7],"y":[3,5,9],"z":[2,4,8],"text":["A","B","C"],"textposition":"top left","marker":{"size":[20,40,30]}}];""" + |> chartGeneratedContains Bubble3D.``Simple Bubble3D chart with axis titles`` + ); + testCase "Bubble layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"x-axis"}},"yaxis":{"title":{"text":"y-axis"}},"zaxis":{"title":{"text":"z-axis"}}}};""" + |> chartGeneratedContains Bubble3D.``Simple Bubble3D chart with axis titles`` + ); + ] + ] + +module Surface = + [] + let ``Surface chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Surface" [ + testCase "First surface data" ( fun () -> + // the data generated by this chart generates a + // line of code which is 200 Kb long, unreasonably + // huge for a test. + [ + "var data = [{\"type\":\"surface\",\"z\":[[0.3929110807586562,0.39272903726671055,0.3923748718856843,0.3918384347714509,0.39110921172503726,0.39017633288191317,0.38902858492457726" + "-0.3755066766683234,-0.38085145315419006,-0.38575670319102695,-0.3902383753762268,-0.3943127362115111,-0.3979962450330162,-0.4013054412792835" + "-0.3901763328819132,-0.39110921172503726,-0.39183843477145097,-0.3923748718856844,-0.39272903726671055,-0.3929110807586562]]}];" + ] + |> chartGeneratedContainsList Surface.``Peak and sink surface plot`` + ); + testCase "First surface layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" + |> chartGeneratedContains Surface.``Peak and sink surface plot`` + ); + testCase "Second surface data" ( fun () -> + """var data = [{"type":"surface","opacity":0.5,"x":[0.0,2.5],"y":[0.0,2.5],"z":[[1.0,1.0],[1.0,2.0]],"contours":{"x":{"show":true},"y":{"show":true},"z":{"show":true}}}];""" + |> chartGeneratedContains Surface.``Surface plot with x/y indices mapping to z matrix with contours`` + ); + testCase "Second surface layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" + |> chartGeneratedContains Surface.``Surface plot with x/y indices mapping to z matrix with contours`` + ); + ] + ] + +module Mesh3D = + [] + let ``Mesh3D chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Mesh3D" [ + testCase "Mesh data" ( fun () -> + """var data = [{"type":"mesh3d","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382],"y":[0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116],"z":[0.1348700468125148,0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959],"contour":{"show":true},"flatshading":true}];""" + |> chartGeneratedContains Mesh3D.``Mesh3D chart with random x/x/z data`` + ); + testCase "Mesh layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" + |> chartGeneratedContains Mesh3D.``Mesh3D chart with random x/x/z data`` + ); + ] + ] + +module Cone = + [] + let ``Cone chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Cone" [ + testCase "Cone data" ( fun () -> + """var data = [{"type":"cone","x":[1,1,1],"y":[1,2,3],"z":[1,1,1],"u":[1,2,3],"v":[1,1,2],"w":[4,4,1],"colorscale":"Viridis"}];""" + |> chartGeneratedContains Cone.``Simple cone chart`` + ); + testCase "Cone layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" + |> chartGeneratedContains Cone.``Simple cone chart`` + ); + ] + ] + +module StreamTube = + [] + let ``StreamTube chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "StreamTube" [ + testCase "StreamTube data" ( fun () -> + """var data = [{"type":"streamtube","x":[0,0,0],"y":[0,1,2],"z":[0,0,0],"u":[0,0,0],"v":[1,1,1],"w":[0,0,0],"colorscale":"Viridis"}];""" + |> chartGeneratedContains StreamTube.``Simple StreamTube chart `` + ); + testCase "StreamTube layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" + |> chartGeneratedContains StreamTube.``Simple StreamTube chart `` + ); + ] + ] + +module Volume = + [] + let ``Volume chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "Volume" [ + testCase "Volume data" ( fun () -> + """var data = [{"type":"volume","x":[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0],"y":[1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0],"z":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"value":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"colorscale":"Viridis"}];""" + |> chartGeneratedContains Volume.``Fancy mgrid based volume chart`` + ); + testCase "Volume layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" + |> chartGeneratedContains Volume.``Fancy mgrid based volume chart`` + ); + ] + ] + +module IsoSurface = + [] + let ``IsoSurface chart HTML codegeneration tests`` = + testList "HTMLCodegen.Chart3D" [ + testList "IsoSurface" [ + testCase "IsoSurface data" ( fun () -> + """var data = [{"type":"isosurface","x":[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0],"y":[1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0],"z":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"value":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"colorscale":"Viridis"}];""" + |> chartGeneratedContains IsoSurface.``Fancy mgrid based isosurface chart`` + ); + testCase "IsoSurface layout" ( fun () -> + """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" + |> chartGeneratedContains IsoSurface.``Fancy mgrid based isosurface chart`` + ); + ] + ] + diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/ChartCarpet.fs b/tests/CoreTests/CoreTests/HTMLCodegen/ChartCarpet.fs new file mode 100644 index 000000000..0c52e7b7b --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/ChartCarpet.fs @@ -0,0 +1,116 @@ +module CoreTests.HTMLCodegen.ChartCarpet + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open ChartCarpetTestCharts + +module Carpet = + + [] + let ``Carpet chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartCarpet" [ + testList "Carpet" [ + ] + ] + +module ScatterCarpet = + + [] + let ``ScatterCarpet chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartCarpet" [ + testList "ScatterCarpet" [ + testCase "ScatterCarpet data" ( fun () -> + """var data = [{"type":"carpet","y":[2.0,3.5,4.0,3.0,4.5,5.0,5.5,6.5,7.5,8.0,8.5,10.0],"a":[4.0,4.0,4.0,4.5,4.5,4.5,5.0,5.0,5.0,6.0,6.0,6.0],"b":[1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0],"carpet":"carpet1"},{"type":"scattercarpet","name":"Scatter","mode":"lines+markers","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{"color":["rgba(255, 0, 0, 1.0)","rgba(0, 0, 255, 1.0)","rgba(0, 128, 0, 1.0)","rgba(255, 255, 0, 1.0)"],"symbol":["46","12","32","15"]},"line":{},"carpet":"carpet1"}];""" + |> chartGeneratedContains ScatterCarpet.``Simple carpet scatter chart`` + ); + testCase "ScatterCarpet layout" ( fun () -> + emptyLayout ScatterCarpet.``Simple carpet scatter chart`` + ); + ] + ] + +module PointCarpet = + + [] + let ``PointCarpet chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartCarpet" [ + testList "PointCarpet" [ + testCase "PointCarpet data" ( fun () -> + """var data = [{"type":"carpet","y":[12.0,13.5,14.0,13.0,14.5,15.0,15.5,16.5,17.5,18.0,18.5,20.0],"a":[6.0,6.0,6.0,5.0,5.0,5.0,4.5,4.5,4.5,4.0,4.0,4.0],"b":[3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0],"carpet":"carpet2"},{"type":"scattercarpet","name":"Point","mode":"markers","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{},"line":{},"carpet":"carpet2"}];""" + |> chartGeneratedContains PointCarpet.``Simple carpet point chart`` + ); + testCase "PointCarpet layout" ( fun () -> + emptyLayout PointCarpet.``Simple carpet point chart`` + ); + ] + ] + +module LineCarpet = + + [] + let ``LineCarpet chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartCarpet" [ + testList "LineCarpet" [ + testCase "LineCarpet data" ( fun () -> + """var data = [{"type":"carpet","y":[22.0,23.5,24.0,23.0,24.5,25.0,25.5,26.5,27.5,28.0,28.5,30.0],"a":[4.0,4.0,4.0,4.5,4.5,4.5,5.0,5.0,5.0,6.0,6.0,6.0],"b":[1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0],"carpet":"carpet3"},{"type":"scattercarpet","name":"Line","mode":"lines","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{},"line":{},"carpet":"carpet3"}];""" + |> chartGeneratedContains LineCarpet.``Simple carpet line chart`` + ); + testCase "LineCarpet layout" ( fun () -> + emptyLayout LineCarpet.``Simple carpet line chart`` + ); + ] + ] + +module SplineCarpet = + + [] + let ``SplineCarpet chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartCarpet" [ + testList "SplineCarpet" [ + testCase "SplineCarpet data" ( fun () -> + """var data = [{"type":"carpet","y":[32.0,33.5,34.0,33.0,34.5,35.0,35.5,36.5,37.5,38.0,38.5,40.0],"a":[6.0,6.0,6.0,5.0,5.0,5.0,4.5,4.5,4.5,4.0,4.0,4.0],"b":[3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0],"carpet":"carpet4"},{"type":"scattercarpet","name":"Spline","mode":"lines","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{},"line":{"shape":"spline"},"carpet":"carpet4"}];""" + |> chartGeneratedContains SplineCarpet.``Simple carpet spline chart`` + ); + testCase "SplineCarpet layout" ( fun () -> + emptyLayout SplineCarpet.``Simple carpet spline chart`` + ); + ] + ] + +module BubbleCarpet = + + [] + let ``BubbleCarpet chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartCarpet" [ + testList "BubbleCarpet" [ + testCase "BubbleCarpet data" ( fun () -> + """var data = [{"type":"carpet","y":[42.0,43.5,44.0,43.0,44.5,45.0,45.5,46.5,47.5,48.0,48.5,50.0],"a":[4.0,4.0,4.0,4.5,4.5,4.5,5.0,5.0,5.0,6.0,6.0,6.0],"b":[1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0],"carpet":"carpet5"},{"type":"scattercarpet","name":"Bubble","mode":"markers","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{"size":[5,10,15,20]},"line":{},"carpet":"carpet5"}];""" + |> chartGeneratedContains BubbleCarpet.``Simple carpet bubble chart`` + ); + testCase "BubbleCarpet layout" ( fun () -> + emptyLayout BubbleCarpet.``Simple carpet bubble chart`` + ); + ] + ] + +module ContourCarpet = + + [] + let ``ContourCarpet chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartCarpet" [ + testList "ContourCarpet" [ + testCase "ContourCarpet data" ( fun () -> + """var data = [{"type":"carpet","opacity":0.75,"x":[2.0,3.0,4.0,5.0,2.2,3.1,4.1,5.1,1.5,2.5,3.5,4.5],"y":[1.0,1.4,1.6,1.75,2.0,2.5,2.7,2.75,3.0,3.5,3.7,3.75],"a":[0.0,1.0,2.0,3.0,0.0,1.0,2.0,3.0,0.0,1.0,2.0,3.0],"b":[4.0,4.0,4.0,4.0,5.0,5.0,5.0,5.0,6.0,6.0,6.0,6.0],"aaxis":{"type":"linear","tickprefix":"a = ","minorgridcount":9,"smoothing":0.0},"baxis":{"type":"linear","tickprefix":"b = ","minorgridcount":9,"smoothing":0.0},"carpet":"contour"},{"type":"contourcarpet","z":[1.0,1.96,2.56,3.0625,4.0,5.0625,1.0,7.5625,9.0,12.25,15.21,14.0625],"a":[0,1,2,3,0,1,2,3,0,1,2,3],"b":[4,4,4,4,5,5,5,5,6,6,6,6],"line":{"color":"rgba(255, 255, 255, 1.0)"},"carpet":"contour","contours":{"showlabels":true}}];""" + |> chartGeneratedContains ContourCarpet.``Styled carpet contour chart`` + ); + testCase "ScatterCarpet layout" ( fun () -> + emptyLayout ContourCarpet.``Styled carpet contour chart`` + ); + ] + ] + diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/ChartDomain.fs b/tests/CoreTests/CoreTests/HTMLCodegen/ChartDomain.fs new file mode 100644 index 000000000..5b44db3b3 --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/ChartDomain.fs @@ -0,0 +1,260 @@ +module CoreTests.HTMLCodegen.ChartDomain + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open ChartDomainTestCharts + +module Pie = + [] + let ``Pie chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Pie" [ + testCase "Pie data" ( fun () -> + """var data = [{"type":"pie","values":[19,26,55],"labels":["Residential","Non-Residential","Utility"],"marker":{"line":{}}}];""" + |> chartGeneratedContains Pie.``Simple pie chart`` + ); + testCase "Pie layout" ( fun () -> + emptyLayout Pie.``Simple pie chart`` + ); + testCase "Pie styled data" ( fun () -> + """var data = [{"type":"pie","values":[19,26,55],"labels":["Residential","Non-Residential","Utility"],"pull":[0.0,0.3,0.0],"text":["Some","More","Stuff"],"textposition":["inside","outside","inside"],"marker":{"colors":["rgba(0, 255, 255, 1.0)","rgba(250, 128, 114, 1.0)","rgba(210, 180, 140, 1.0)"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":2.0}},"rotation":45.0}];""" + |> chartGeneratedContains Pie.``Styled pie chart`` + ); + testCase "Pie styled layout" ( fun () -> + emptyLayout Pie.``Styled pie chart`` + ); + ] + ] + +module Doughnut = + [] + let ``Doughnut chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Doughnut" [ + testCase "Doughnut data" ( fun () -> + """var data = [{"type":"pie","values":[19,26,55],"labels":["Residential","Non-Residential","Utility"],"text":["Residential","Non-Residential","Utility"],"marker":{"line":{}},"hole":0.3}];""" + |> chartGeneratedContains Doughnut.``Simple doughnut chart`` + ); + testCase "Doughnut layout" ( fun () -> + emptyLayout Doughnut.``Simple doughnut chart`` + ); + ] + ] + +module FunnelArea = + [] + let ``FunnelArea chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "FunnelArea" [ + testCase "Funnel area data" ( fun () -> + """var data = [{"type":"funnelarea","values":[5,4,3,2,1],"text":["The 1st","The 2nd","The 3rd","The 4th","The 5th"],"marker":{"line":{"color":"purple","width":3.0}}}];""" + |> chartGeneratedContains FunnelArea.``Simple funnelarea chart`` + ); + testCase "Funnel area layout" ( fun () -> + emptyLayout FunnelArea.``Simple funnelarea chart`` + ); + testCase "Funnel area styled data" ( fun () -> + """var data = [{"type":"funnelarea","values":[5,4,3],"labels":["The 1st","The 2nd","The 3rd"],"text":["The 1st","The 2nd","The 3rd"],"marker":{"colors":["rgba(0, 255, 255, 1.0)","rgba(250, 128, 114, 1.0)","rgba(210, 180, 140, 1.0)"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":2.0}},"aspectratio":0.75,"baseratio":0.1}];""" + |> chartGeneratedContains FunnelArea.``Styled funnelarea chart`` + ); + testCase "Funnel area styled layout" ( fun () -> + emptyLayout FunnelArea.``Styled funnelarea chart`` + ); + ] + ] + +module Sunburst = + [] + let ``Sunburst chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Sunburst" [ + testCase "Sunburst data" ( fun () -> + """var data = [{"type":"sunburst","parents":["","","B","B",""],"values":[5.0,0.0,3.0,2.0,3.0],"labels":["A","B","C","D","E"],"text":["At","Bt","Ct","Dt","Et"],"marker":{"line":{}}}];""" + |> chartGeneratedContains Sunburst.``Simple sunburst chart`` + ); + testCase "Sunburst layout" ( fun () -> + emptyLayout Sunburst.``Simple sunburst chart`` + ); + testCase "Sunburst styled data" ( fun () -> + """var data = [{"type":"sunburst","parents":["","","","","","A","A","B","AA","AA"],"values":[20,1,2,3,4,15,5,1,10,5],"labels":["A","B","C","D","E","AA","AB","BA","AAA","AAB"],"marker":{"colorscale":"Viridis","line":{"color":"rgba(0, 0, 0, 1.0)"},"showscale":true},"branchvalues":"total","rotation":45}];""" + |> chartGeneratedContains Sunburst.``Styled sunburst chart`` + ); + testCase "Sunburst styled layout" ( fun () -> + emptyLayout Sunburst.``Styled sunburst chart`` + ); + ] + ] + +module Treemap = + [] + let ``Treemap chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Treemap" [ + testCase "Treemap styled data" ( fun () -> + """var data = [{"type":"treemap","parents":["","","","","","A","A","B","AA","AA"],"values":[20,1,2,3,4,15,5,1,10,5],"labels":["A","B","C","D","E","AA","AB","BA","AAA","AAB"],"marker":{"colorscale":"Viridis","line":{"color":"rgba(0, 0, 0, 1.0)"},"showscale":true},"branchvalues":"total","tiling":{"packing":"slice-dice"}}];""" + |> chartGeneratedContains Treemap.``Styled treemap chart`` + ); + testCase "Treemap styled layout" ( fun () -> + emptyLayout Treemap.``Styled treemap chart`` + ); + ] + ] + +module ParallelCoord = + [] + let ``ParallelCoord chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "ParallelCoord" [ + testCase "Parallel coordinates data" ( fun () -> + """var data = [{"type":"parcoords","dimensions":[{"label":"A","values":[1.0,4.0,3.4,0.7],"axis":{}},{"label":"B","values":[3.0,1.5,1.7,2.3],"axis":{}},{"label":"C","values":[2.0,4.0,3.1,5.0],"axis":{}},{"label":"D","values":[4.0,2.0,2.0,4.0],"axis":{}}],"line":{"color":"blue"}}];""" + |> chartGeneratedContains ParallelCoord.``Simple parallel coordinates chart`` + ); + testCase "Parallel coordinates layout" ( fun () -> + emptyLayout ParallelCoord.``Simple parallel coordinates chart`` + ); + testCase "Parallel coordinates styled data" ( fun () -> + """var data = [{"type":"parcoords","dimensions":[{"label":"1","values":[1,2,3,4],"range":[0.0,8.0],"axis":{}},{"label":"2","values":[4,3,2,1],"range":[0.0,8.0],"axis":{}},{"label":"3","values":[1,2,3,4],"range":[0.0,8.0],"axis":{}},{"label":"4","values":[4,3,2,1],"range":[0.0,8.0],"axis":{}}],"line":{"color":[1,2,3,4],"colorscale":"Viridis"}}];""" + |> chartGeneratedContains ParallelCoord.``Styled parallel coordinates chart`` + ); + testCase "Parallel coordinates styled layout" ( fun () -> + emptyLayout ParallelCoord.``Styled parallel coordinates chart`` + ); + ] + ] + +module ParallelCategories = + [] + let ``ParallelCategories chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "ParallelCategories" [ + testCase "Parallel categories data" ( fun () -> + """var data = [{"type":"parcats","dimensions":[{"label":"A","values":["Cat1","Cat1","Cat1","Cat1","Cat2","Cat2","Cat3"],"axis":{}},{"label":"B","values":[0,1,0,1,0,0,0],"ticktext":["YES","NO"],"axis":{}}],"line":{"color":[0.0,1.0,0.0,1.0,0.0,0.0,0.0],"colorscale":"Blackbody"}}];""" + |> chartGeneratedContains ParallelCategories.``Simple parallel categories chart`` + ); + testCase "Parallel categories layout" ( fun () -> + emptyLayout ParallelCategories.``Simple parallel categories chart`` + ); + testCase "Parallel categories styled data" ( fun () -> + """var data = [{"type":"parcats","dimensions":[{"label":"Lvl1","values":["A","A","A","B","B","B","C","D"],"axis":{}},{"label":"Lvl2","values":["AA","AA","AB","AB","AB","AB","AB","AB"],"axis":{}},{"label":"Lvl3","values":["AAA","AAB","AAC","AAC","AAB","AAB","AAA","AAA"],"axis":{}}],"line":{"color":[0,1,2,2,1,1,0,0],"colorscale":"Viridis"},"bundlecolors":false}];""" + |> chartGeneratedContains ParallelCategories.``Styled parallel categories chart`` + ); + testCase "Parallel categories styled layout" ( fun () -> + emptyLayout ParallelCategories.``Styled parallel categories chart`` + ); + ] + ] + +module Sankey = + [] + let ``Sankey chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Sankey" [ + testCase "Sankey data" ( fun () -> + """var data = [{"type":"sankey","node":{"label":["A1","A2","B1","B2","C1","C2","D1"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":1.0}},"link":{"color":["rgba(130, 139, 251, 1.0)","rgba(130, 139, 251, 1.0)","rgba(242, 119, 98, 1.0)","rgba(51, 214, 171, 1.0)","rgba(188, 130, 251, 1.0)","rgba(188, 130, 251, 1.0)","rgba(255, 180, 123, 1.0)","rgba(71, 220, 245, 1.0)"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":1.0},"source":[0,0,1,2,3,3,4,5],"target":[2,3,3,4,4,5,6,6],"value":[8,4,2,7,3,2,5,2]}}];""" + |> chartGeneratedContains Sankey.``Styled sankey chart`` + ) + testCase "Sankey layout" ( fun () -> + emptyLayout Sankey.``Styled sankey chart`` + ) + ] + ] + +module Table = + [] + let ``Table chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Table" [ + testCase "First table data" ( fun () -> + """var data = [{"type":"table","cells":{"fill":{},"line":{},"values":[["0","1"],["I","little"],["am","example"],["a","!"]]},"header":{"fill":{},"line":{},"values":["RowIndex","A","simple","table"]}}];""" + |> chartGeneratedContains Table.``Simple table chart`` + ); + testCase "First table layout" ( fun () -> + emptyLayout Table.``Simple table chart`` + ); + testCase "Styled table data" ( fun () -> + """var data = [{"type":"table","columnorder":[1,2,3,4],"columnwidth":[70.0,50.0,100.0,70.0],"cells":{"align":["left","center","right"],"fill":{"color":["#deebf7","lightgrey","#deebf7","lightgrey"]},"line":{},"values":[["0","1"],["I","little"],["am","example"],["a","!"]]},"header":{"align":"center","fill":{"color":"#45546a"},"height":30,"line":{"color":"black","width":2.0},"values":["RowIndex","A","simple","table"]}}];""" + |> chartGeneratedContains Table.``Styled table chart`` + ); + testCase "Styled table layout" ( fun () -> + emptyLayout Table.``Styled table chart`` + ); + testCase "Color dependent chart data" ( fun () -> + """var data = [{"type":"table","cells":{"fill":{"color":[["white","white","white","white","white","white","white"],["rgba(255, 255, 0, 1.0)","rgba(255, 245, 10, 1.0)","rgba(255, 204, 51, 1.0)","rgba(255, 204, 51, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 148, 107, 1.0)","rgba(255, 26, 229, 1.0)"],["rgba(255, 250, 5, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 174, 81, 1.0)","rgba(255, 215, 40, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 102, 153, 1.0)"],["rgba(255, 240, 15, 1.0)","rgba(255, 51, 204, 1.0)","rgba(255, 164, 91, 1.0)","rgba(255, 179, 76, 1.0)","rgba(255, 148, 107, 1.0)","rgba(255, 164, 91, 1.0)","rgba(255, 153, 102, 1.0)"],["rgba(255, 245, 10, 1.0)","rgba(255, 0, 255, 1.0)","rgba(255, 143, 112, 1.0)","rgba(255, 220, 35, 1.0)","rgba(255, 159, 96, 1.0)","rgba(255, 148, 107, 1.0)","rgba(255, 128, 127, 1.0)"]]},"line":{},"values":[[10004.0,10001.0,10005.0,10006.0,10007.0,10002.0,10003.0],[0.0,0.2,1.0,1.0,2.0,2.1,4.5],[0.1,2.0,1.6,0.8,2.0,2.0,3.0],[0.3,4.0,1.8,1.5,2.1,1.8,2.0],[0.2,5.0,2.2,0.7,1.9,2.1,2.5]]},"header":{"fill":{},"line":{},"values":["Identifier","T0","T1","T2","T3"]}}];""" + |> chartGeneratedContains Table.``Cell color dependent table chart`` + ); + testCase "Color dependent chart layout" ( fun () -> + emptyLayout Table.``Cell color dependent table chart`` + ); + testCase "Sequence presentation table data" ( fun () -> + """var data = [{"type":"table","columnwidth":[50.0,10.0],"cells":{"align":["right","center"],"fill":{"color":[["white","white","white","white","white"],["#5050FF","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E00000","#E6E600","#E00000","white"],["#00C000","#00C000","#E00000","#00C000","white"],["#5050FF","#E6E600","#00C000","#E6E600","white"],["#00C000","#E00000","#5050FF","#E00000","white"],["#5050FF","#00C000","#E6E600","#00C000","white"],["#E00000","#5050FF","#5050FF","#5050FF","white"],["#00C000","#E6E600","#00C000","#E6E600","white"],["#E6E600","#5050FF","#5050FF","#5050FF","white"],["#E00000","#00C000","#E00000","#00C000","white"],["#00C000","#5050FF","#00C000","#5050FF","white"],["#5050FF","#E00000","#E6E600","#E00000","white"],["#00C000","#00C000","#E00000","#E00000","white"],["#5050FF","#E6E600","#00C000","#00C000","white"],["#E00000","#E00000","#5050FF","#E6E600","white"],["#E6E600","#00C000","#E6E600","#5050FF","white"],["#00C000","#5050FF","#5050FF","#00C000","white"],["#5050FF","#E6E600","#00C000","#5050FF","white"],["#E6E600","#5050FF","#5050FF","#E00000","white"],["#5050FF","#00C000","#00C000","#00C000","white"],["#00C000","#5050FF","#5050FF","#E6E600","white"],["#5050FF","#00C000","#E6E600","#E00000","white"],["#E00000","#E6E600","#5050FF","#00C000","white"],["#00C000","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E6E600","#5050FF","#E6E600","white"],["#E00000","#5050FF","#E00000","#5050FF","white"],["#00C000","#00C000","#00C000","#00C000","white"],["#5050FF","#5050FF","#E6E600","#5050FF","white"],["#E6E600","#E00000","#E00000","#E00000","white"],["#5050FF","#E00000","#00C000","#00C000","white"],["#00C000","#00C000","#5050FF","#E6E600","white"],["#5050FF","#E6E600","#E6E600","#E00000","white"],["#E00000","#00C000","#5050FF","#00C000","white"],["#00C000","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E6E600","#5050FF","#E6E600","white"],["#E00000","#5050FF","#E00000","#5050FF","white"],["#00C000","#00C000","#E00000","#00C000","white"],["#5050FF","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E00000","#E6E600","#E00000","white"],["#5050FF","#00C000","#5050FF","#E00000","white"],["#00C000","#E6E600","#E6E600","#00C000","white"],["#5050FF","#E00000","#5050FF","#E6E600","white"],["#E00000","#00C000","#00C000","white"],["#E00000","#5050FF","#5050FF","white"],["#00C000","#00C000","#5050FF","white"],["#5050FF","#5050FF","#00C000","white"],["#E6E600","#5050FF","#5050FF","white"],["#5050FF","#00C000","#E00000","white"],["#00C000","#5050FF","#00C000","white"],["#5050FF","#E00000","#E6E600","white"],["#E00000","#00C000","#E00000","white"],["#E6E600","#E6E600","#00C000","white"],["#E00000","#E00000","#5050FF","white"],["#00C000","#00C000","#E6E600","white"],["#E6E600","#5050FF","#5050FF","white"],["#00C000","#E6E600","#00C000","white"],["#5050FF","#5050FF","#5050FF","white"],["#E6E600","#00C000","#E6E600","white"],["#5050FF","#5050FF","#5050FF","white"],["#00C000","#E00000","#00C000","white"]]},"height":20,"line":{"color":"white","width":0.0},"values":[["0","60","120","180"],["A","A","G","A"],["T","C","T","C"],["G","G","C","G"],["A","T","G","T"],["G","C","A","C"],["A","G","T","G"],["C","A","A","A"],["G","T","G","T"],["T","A","A","A"],["C","G","C","G"],["G","A","G","A"],["A","C","T","C"],["G","G","C","C"],["A","T","G","G"],["C","C","A","T"],["T","G","T","A"],["G","A","A","G"],["A","T","G","A"],["T","A","A","C"],["A","G","G","G"],["G","A","A","T"],["A","G","T","C"],["C","T","A","G"],["G","A","G","A"],["T","T","A","T"],["C","A","C","A"],["G","G","G","G"],["A","A","T","A"],["T","C","C","C"],["A","C","G","G"],["G","G","A","T"],["A","T","T","C"],["C","G","A","G"],["G","A","G","A"],["T","T","A","T"],["C","A","C","A"],["G","G","C","G"],["A","A","G","A"],["T","C","T","C"],["A","G","A","C"],["G","T","T","G"],["A","C","A","T"],["C","G","G"],["C","A","A"],["G","G","A"],["A","A","G"],["T","A","A"],["A","G","C"],["G","A","G"],["A","C","T"],["C","G","C"],["T","T","G"],["C","C","A"],["G","G","T"],["T","A","A"],["G","T","G"],["A","A","A"],["T","G","T"],["A","A","A"],["G","C","G"]]},"header":{"fill":{},"line":{"color":"white","width":0.0},"values":["","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|"]}}];""" + |> chartGeneratedContains Table.``Sequence representation table chart`` + ); + testCase "Sequence presentation table layout" ( fun () -> + "var layout = {\"width\":650,\"title\":{\"text\":\"Sequence A\"}};" + |> chartGeneratedContains Table.``Sequence representation table chart`` + ); + ] + ] + +module Indicator = + [] + let ``Indicator chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Indicator" [ + testCase "Angular gauge indicator data" ( fun () -> + """var data = [{"type":"indicator","mode":"number+delta+gauge","value":200.0,"domain":{"row":0,"column":0},"delta":{"reference":160},"gauge":{"axis":{"range":[0.0,250.0]}}}];""" + |> chartGeneratedContains Indicator.``Angular gauge indicator`` + ); + testCase "Angular gauge indicator layout" ( fun () -> + emptyLayout Indicator.``Angular gauge indicator`` + ); + testCase "Bullet gauge indicator data" ( fun () -> + """var data = [{"type":"indicator","mode":"number+delta+gauge","value":120,"domain":{"row":0,"column":1},"delta":{"reference":90},"gauge":{"axis":{"visible":false,"range":[-200.0,200.0]},"shape":"bullet"}}];""" + |> chartGeneratedContains Indicator.``Bullet gauge indicator`` + ); + testCase "Bullet gauge indicator layout" ( fun () -> + emptyLayout Indicator.``Bullet gauge indicator`` + ); + testCase "Delta indicator with reference data" ( fun () -> + """var data = [{"type":"indicator","mode":"number+delta","value":"300","domain":{"row":1,"column":0},"delta":{"reference":90.0},"gauge":{"axis":{}}}];""" + |> chartGeneratedContains Indicator.``Delta indicator with reference`` + ); + testCase "Delta indicator with reference layout" ( fun () -> + emptyLayout Indicator.``Delta indicator with reference`` + ); + testCase "Delta indicator data" ( fun () -> + """var data = [{"type":"indicator","mode":"delta","value":40.0,"domain":{"row":1,"column":1},"delta":{"reference":90.0},"gauge":{"axis":{}}}];""" + |> chartGeneratedContains Indicator.``Delta indicator`` + ); + testCase "Delta indicator layout" ( fun () -> + emptyLayout Indicator.``Delta indicator`` + ); + ] + ] + +module Icicle = + [] + let ``Icicle chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartDomain" [ + testList "Icicle" [ + testCase "Icicle data" ( fun () -> + """var data = [{"type":"icicle","parents":["","Eve","Eve","Seth","Seth","Eve","Eve","Awan","Eve"],"labels":["Eve","Cain","Seth","Enos","Noam","Abel","Awan","Enoch","Azura"],"marker":{"colorscale":"Viridis","line":{},"showscale":true},"tiling":{"flip":"y","orientation":"v"},"pathbar":{"edgeshape":"\\"}}];""" + |> chartGeneratedContains Icicle.``Simple icicle chart`` + ) + testCase "Icicle layout" ( fun () -> + emptyLayout Icicle.``Simple icicle chart`` + ) + testCase "Icicle styled data" ( fun () -> + """var data = [{"type":"icicle","parents":["","","","","","A","A","B","AA","AA"],"values":[20,1,2,3,4,15,5,1,10,5],"labels":["A","B","C","D","E","AA","AB","BA","AAA","AAB"],"marker":{"colorscale":"Viridis","line":{"color":"rgba(0, 0, 0, 1.0)"},"showscale":true},"branchvalues":"total","tiling":{"flip":"x+y"},"pathbar":{}}];""" + |> chartGeneratedContains Icicle.``Styled icicle chart`` + ) + testCase "Icicle styled layout" ( fun () -> + emptyLayout Icicle.``Styled icicle chart`` + ) + ] + ] diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/ChartLayout.fs b/tests/CoreTests/CoreTests/HTMLCodegen/ChartLayout.fs similarity index 100% rename from tests/Plotly.NET.Tests/HtmlCodegen/ChartLayout.fs rename to tests/CoreTests/CoreTests/HTMLCodegen/ChartLayout.fs diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/ChartMap.fs b/tests/CoreTests/CoreTests/HTMLCodegen/ChartMap.fs new file mode 100644 index 000000000..70f93e70d --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/ChartMap.fs @@ -0,0 +1,200 @@ +module CoreTests.HTMLCodegen.ChartMap + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open ChartMapTestCharts + +module GeoBaseMap = + [] + let ``GeoBaseMap chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "GeoBaseMap" [ + testCase "Basemap data" ( fun () -> + """var data = [{"type":"scattergeo","mode":"markers","locations":[],"marker":{},"line":{}}];""" + |> chartGeneratedContains GeoBaseMap.``Base map only`` + ); + testCase "Basemap layout" ( fun () -> + "var layout = {\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" + |> chartGeneratedContains GeoBaseMap.``Base map only`` + ); + testCase "More features map data" ( fun () -> + """var data = [{"type":"scattergeo","mode":"markers","locations":[],"marker":{},"line":{}}];""" + |> chartGeneratedContains GeoBaseMap.``styled base map only`` + ); + testCase "More features map layout" ( fun () -> + "var layout = {\"geo\":{\"resolution\":\"50\",\"showcoastline\":true,\"coastlinecolor\":\"RebeccaPurple\",\"showland\":true,\"landcolor\":\"LightGreen\",\"showocean\":true,\"oceancolor\":\"LightBlue\",\"showlakes\":true,\"lakecolor\":\"Blue\",\"showrivers\":true,\"rivercolor\":\"Blue\"},\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" + |> chartGeneratedContains GeoBaseMap.``styled base map only`` + ); + testCase "Cultural map data" ( fun () -> + """var data = [{"type":"scattergeo","mode":"markers","locations":[],"marker":{},"line":{}}];""" + |> chartGeneratedContains GeoBaseMap.``Base map country borders at 1:50m resolution`` + ); + testCase "Cultural map layout" ( fun () -> + "var layout = {\"geo\":{\"resolution\":\"50\",\"visible\":false,\"showcountries\":true,\"countrycolor\":\"RebeccaPurple\"},\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" + |> chartGeneratedContains GeoBaseMap.``Base map country borders at 1:50m resolution`` + ); + ] + ] + +module ChoroplethMap = + [] + let ``ChoroplethMap chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "ChoroplethMap" [ + testCase "Choropleth map 1 data" ( fun () -> + """var data = [{"type":"choropleth","z":[17.5,16.8,15.4,15.1,14.4,13.9,13.8,13.3,13.0,13.0,12.9,12.6,12.5,12.5,12.3,12.3,12.3,12.2,12.2,12.2,11.9,11.9,11.8,11.6,11.6,11.4,11.4,11.2,11.0,11.0,10.9,10.9,10.8,10.7,10.4,10.3,10.3,10.3,10.3,10.2,10.1,9.9,9.8,9.8,9.6,9.3,9.3,9.2,9.2,9.2,8.9,8.8,8.7,8.7,8.7,8.5,8.4,8.4,8.2,8.1,8.1,8.0,8.0,7.9,7.7,7.7,7.7,7.6,7.5,7.3,7.2,7.2,7.2,7.1,7.1,7.1,7.1,7.1,7.0,7.0,6.9,6.9,6.9,6.9,6.8,6.8,6.7,6.7,6.7,6.7,6.6,6.6,6.6,6.6,6.5,6.4,6.4,6.2,6.0,5.9,5.7,5.7,5.6,5.5,5.4,5.4,5.4,5.4,5.3,5.2,5.0,4.9,4.8,4.7,4.6,4.4,4.3,4.3,4.3,4.3,4.3,4.2,4.0,4.0,4.0,3.9,3.8,3.8,3.7,3.7,3.6,3.6,3.6,3.5,3.4,3.3,3.2,3.0,3.0,3.0,2.8,2.8,2.7,2.5,2.4,2.3,2.3,2.3,2.2,2.1,2.1,2.0,2.0,1.8,1.7,1.6,1.5,1.5,1.5,1.4,1.3,1.3,1.2,1.2,1.1,1.1,1.0,1.0,0.9,0.9,0.9,0.7,0.7,0.7,0.7,0.7,0.6,0.6,0.6,0.5,0.5,0.4,0.3,0.3,0.2,0.2,0.2,0.1,0.1,0.1,0.1],"locations":["Belarus","Moldova","Lithuania","Russia","Romania","Ukraine","Andorra","Hungary","Czech Republic","Slovakia","Portugal","Serbia","Grenada","Poland","Latvia","Finland","South Korea","France","Australia","Croatia","Ireland","Luxembourg","Germany","Slovenia","United Kingdom","Denmark","Bulgaria","Spain","Belgium","South Africa","New Zealand","Gabon","Namibia","Switzerland","Saint Lucia","Austria","Estonia","Greece","Kazakhstan","Canada","Nigeria","Netherlands","Uganda","Rwanda","Chile","Argentina","Burundi","United States","Cyprus","Sweden","Venezuela","Paraguay","Brazil","Sierra Leone","Montenegro","Belize","Cameroon","Botswana","Saint Kitts and Nevis","Guyana","Peru","Panama","Niue","Palau","Norway","Tanzania","Georgia","Uruguay","Angola","Laos","Japan","Mexico","Ecuador","Dominica","Iceland","Thailand","Bosnia and Herzegovina","Sao Tome and Principe","Malta","Albania","Bahamas","Dominican Republic","Mongolia","Cape Verde","Barbados","Burkina Faso","Italy","Trinidad and Tobago","China","Macedonia","Saint Vincent and the Grenadines","Equatorial Guinea","Suriname","Vietnam","Lesotho","Haiti","Cook Islands","Colombia","Ivory Coast","Bolivia","Swaziland","Zimbabwe","Seychelles","Cambodia","Puerto Rico","Netherlands Antilles","Philippines","Costa Rica","Armenia","Cuba","Nicaragua","Jamaica","Ghana","Liberia","Uzbekistan","Chad","United Arab Emirates","Kyrgyzstan","India","Turkmenistan","Kenya","Ethiopia","Honduras","Guinea-Bissau","Zambia","Republic of the Congo","Guatemala","Central African Republic","North Korea","Sri Lanka","Mauritius","Samoa","Democratic Republic of the Congo","Nauru","Gambia","Federated States of Micronesia","El Salvador","Fiji","Papua New Guinea","Kiribati","Tajikistan","Israel","Sudan","Malawi","Lebanon","Azerbaijan","Mozambique","Togo","Nepal","Brunei","Benin","Singapore","Turkey","Madagascar","Solomon Islands","Tonga","Tunisia","Tuvalu","Qatar","Vanuatu","Djibouti","Malaysia","Syria","Maldives","Mali","Eritrea","Algeria","Iran","Oman","Brunei","Morocco","Jordan","Bhutan","Guinea","Burma","Afghanistan","Senegal","Indonesia","Timor-Leste","Iraq","Somalia","Egypt","Niger","Yemen","Comoros","Saudi Arabia","Bangladesh","Kuwait","Libya","Mauritania","Pakistan"],"locationmode":"country names"}];""" + |> chartGeneratedContains ChoroplethMap.``ChoroplethMap chart of top beverage consumption countries`` + ); + testCase "Choropleth map 1 layout" ( fun () -> + emptyLayout ChoroplethMap.``ChoroplethMap chart of top beverage consumption countries`` + ); + testCase "Choropleth map 2 data" ( fun () -> + """var data = [{"type":"choropleth","z":[17.5,16.8,15.4,15.1,14.4,13.9,13.8,13.3,13.0,13.0,12.9,12.6,12.5,12.5,12.3,12.3,12.3,12.2,12.2,12.2,11.9,11.9,11.8,11.6,11.6,11.4,11.4,11.2,11.0,11.0,10.9,10.9,10.8,10.7,10.4,10.3,10.3,10.3,10.3,10.2,10.1,9.9,9.8,9.8,9.6,9.3,9.3,9.2,9.2,9.2,8.9,8.8,8.7,8.7,8.7,8.5,8.4,8.4,8.2,8.1,8.1,8.0,8.0,7.9,7.7,7.7,7.7,7.6,7.5,7.3,7.2,7.2,7.2,7.1,7.1,7.1,7.1,7.1,7.0,7.0,6.9,6.9,6.9,6.9,6.8,6.8,6.7,6.7,6.7,6.7,6.6,6.6,6.6,6.6,6.5,6.4,6.4,6.2,6.0,5.9,5.7,5.7,5.6,5.5,5.4,5.4,5.4,5.4,5.3,5.2,5.0,4.9,4.8,4.7,4.6,4.4,4.3,4.3,4.3,4.3,4.3,4.2,4.0,4.0,4.0,3.9,3.8,3.8,3.7,3.7,3.6,3.6,3.6,3.5,3.4,3.3,3.2,3.0,3.0,3.0,2.8,2.8,2.7,2.5,2.4,2.3,2.3,2.3,2.2,2.1,2.1,2.0,2.0,1.8,1.7,1.6,1.5,1.5,1.5,1.4,1.3,1.3,1.2,1.2,1.1,1.1,1.0,1.0,0.9,0.9,0.9,0.7,0.7,0.7,0.7,0.7,0.6,0.6,0.6,0.5,0.5,0.4,0.3,0.3,0.2,0.2,0.2,0.1,0.1,0.1,0.1],"locations":["Belarus","Moldova","Lithuania","Russia","Romania","Ukraine","Andorra","Hungary","Czech Republic","Slovakia","Portugal","Serbia","Grenada","Poland","Latvia","Finland","South Korea","France","Australia","Croatia","Ireland","Luxembourg","Germany","Slovenia","United Kingdom","Denmark","Bulgaria","Spain","Belgium","South Africa","New Zealand","Gabon","Namibia","Switzerland","Saint Lucia","Austria","Estonia","Greece","Kazakhstan","Canada","Nigeria","Netherlands","Uganda","Rwanda","Chile","Argentina","Burundi","United States","Cyprus","Sweden","Venezuela","Paraguay","Brazil","Sierra Leone","Montenegro","Belize","Cameroon","Botswana","Saint Kitts and Nevis","Guyana","Peru","Panama","Niue","Palau","Norway","Tanzania","Georgia","Uruguay","Angola","Laos","Japan","Mexico","Ecuador","Dominica","Iceland","Thailand","Bosnia and Herzegovina","Sao Tome and Principe","Malta","Albania","Bahamas","Dominican Republic","Mongolia","Cape Verde","Barbados","Burkina Faso","Italy","Trinidad and Tobago","China","Macedonia","Saint Vincent and the Grenadines","Equatorial Guinea","Suriname","Vietnam","Lesotho","Haiti","Cook Islands","Colombia","Ivory Coast","Bolivia","Swaziland","Zimbabwe","Seychelles","Cambodia","Puerto Rico","Netherlands Antilles","Philippines","Costa Rica","Armenia","Cuba","Nicaragua","Jamaica","Ghana","Liberia","Uzbekistan","Chad","United Arab Emirates","Kyrgyzstan","India","Turkmenistan","Kenya","Ethiopia","Honduras","Guinea-Bissau","Zambia","Republic of the Congo","Guatemala","Central African Republic","North Korea","Sri Lanka","Mauritius","Samoa","Democratic Republic of the Congo","Nauru","Gambia","Federated States of Micronesia","El Salvador","Fiji","Papua New Guinea","Kiribati","Tajikistan","Israel","Sudan","Malawi","Lebanon","Azerbaijan","Mozambique","Togo","Nepal","Brunei","Benin","Singapore","Turkey","Madagascar","Solomon Islands","Tonga","Tunisia","Tuvalu","Qatar","Vanuatu","Djibouti","Malaysia","Syria","Maldives","Mali","Eritrea","Algeria","Iran","Oman","Brunei","Morocco","Jordan","Bhutan","Guinea","Burma","Afghanistan","Senegal","Indonesia","Timor-Leste","Iraq","Somalia","Egypt","Niger","Yemen","Comoros","Saudi Arabia","Bangladesh","Kuwait","Libya","Mauritania","Pakistan"],"locationmode":"country names","colorbar":{"len":0.5,"title":{"text":"Alcohol consumption[l/y]"}}}];""" + |> chartGeneratedContains ChoroplethMap.``ChoroplethMap chart of top beverage consumption countries with styled map and projecton`` + ); + testCase "Choropleth map 2 layout" ( fun () -> + "var layout = {\"geo\":{\"projection\":{\"type\":\"mollweide\"},\"showocean\":true,\"oceancolor\":\"lightblue\",\"showlakes\":true,\"showrivers\":true}};" + |> chartGeneratedContains ChoroplethMap.``ChoroplethMap chart of top beverage consumption countries with styled map and projecton`` + ); + ] + ] + +module ScatterGeo = + [] + let ``ScatterGeo chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "ScatterGeo" [ + ] + ] + +module PointGeo = + [] + let ``PointGeo chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "PointGeo" [ + testCase "Point geo data" ( fun () -> + """var data = [{"type":"scattergeo","mode":"markers+text","lat":[45.5,43.4,49.13,51.1,53.34,45.24,44.64,48.25,49.89,50.45],"lon":[-73.57,-79.24,-123.06,-114.1,-113.28,-75.43,-63.57,-123.21,-97.13,-104.6],"text":["Montreal","Toronto","Vancouver","Calgary","Edmonton","Ottawa","Halifax","Victoria","Winnepeg","Regina"],"textposition":"top center","marker":{},"line":{}}];""" + |> chartGeneratedContains PointGeo.``Point geo chart of canadian cities`` + ); + testCase "Point geo layout" ( fun () -> + "var layout = {\"geo\":{\"scope\":\"north america\",\"projection\":{\"type\":\"azimuthal equal area\"},\"countrycolor\":\"lightgrey\"},\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" + |> chartGeneratedContains PointGeo.``Point geo chart of canadian cities`` + ); + ] + ] + +module LineGeo = + [] + let ``LineGeo chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "LineGeo" [ + testCase "Flight map data" ( fun () -> + """var data = [{"type":"scattergeo","opacity":1.0,"mode":"lines","lat":[32.89595056,35.04022222],"lon":[-97.0372,-106.6091944],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.3738738738738739,"mode":"lines","lat":[41.979595,30.19453278],"lon":[-87.90446417,-97.66987194],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.36486486486486486,"mode":"lines","lat":[32.89595056,41.93887417],"lon":[-97.0372,-72.68322833],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.12612612612612611,"mode":"lines","lat":[18.43941667,41.93887417],"lon":[-66.00183333,-72.68322833],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.3783783783783784,"mode":"lines","lat":[32.89595056,33.56294306],"lon":[-97.0372,-86.75354972],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.12612612612612611,"mode":"lines","lat":[25.79325,36.12447667],"lon":[-80.29055556,-86.67818222],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.9504504504504504,"mode":"lines","lat":[32.89595056,42.3643475],"lon":[-97.0372,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.8828828828828829,"mode":"lines","lat":[25.79325,42.3643475],"lon":[-80.29055556,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.9684684684684685,"mode":"lines","lat":[41.979595,42.3643475],"lon":[-87.90446417,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.12612612612612611,"mode":"lines","lat":[18.43941667,42.3643475],"lon":[-66.00183333,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.0990990990990991,"mode":"lines","lat":[18.33730556,42.3643475],"lon":[-64.97336111,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.25225225225225223,"mode":"lines","lat":[25.79325,39.17540167],"lon":[-80.29055556,-76.66819833],"marker":{"color":"red"},"line":{}}];""" + |> chartGeneratedContains LineGeo.``LineGeo plot of feb. 2011 American Airline flights`` + ); + testCase "Flight map layout" ( fun () -> + "var layout = {\"showlegend\":false,\"geo\":{\"scope\":\"north america\",\"projection\":{\"type\":\"azimuthal equal area\"},\"showland\":true,\"landcolor\":\"lightgrey\"},\"margin\":{\"l\":0,\"r\":0,\"t\":50,\"b\":0},\"title\":{\"text\":\"Feb. 2011 American Airline flights\"}};" + |> chartGeneratedContains LineGeo.``LineGeo plot of feb. 2011 American Airline flights`` + ); + ] + ] + +module BubbleGeo = + [] + let ``BubbleGeo chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "BubbleGeo" [ + ] + ] + +module MapboxBaseMap = + [] + let ``Mapbox base map chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "MapboxBaseMap" [ + testCase "Mapbox open streetmap layer only data" ( fun () -> + """var data = [{"type":"scattermapbox","mode":"markers","lat":[],"lon":[],"cluster":{},"marker":{},"line":{}}];""" + |> chartGeneratedContains MapboxBaseMap.``Open streetmap layer only`` + ); + testCase "Mapbox open streetmap layer only layout" ( fun () -> + "var layout = {\"mapbox\":{\"style\":\"open-street-map\"}};" + |> chartGeneratedContains MapboxBaseMap.``Open streetmap layer only`` + ); + ] + ] + +module ScatterMapbox = + [] + let ``ScatterMapbox chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "ScatterMapbox" [ + ] + ] + +module PointMapbox = + [] + let ``PointMapbox chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "PointMapbox" [ + testCase "Point mapbox data" ( fun () -> + """var data = [{"type":"scattermapbox","mode":"markers+text","lat":[45.5,43.4,49.13,51.1,53.34,45.24,44.64,48.25,49.89,50.45],"lon":[-73.57,-79.24,-123.06,-114.1,-113.28,-75.43,-63.57,-123.21,-97.13,-104.6],"cluster":{},"text":["Montreal","Toronto","Vancouver","Calgary","Edmonton","Ottawa","Halifax","Victoria","Winnepeg","Regina"],"textposition":"top center","marker":{},"line":{}}];""" + |> chartGeneratedContains PointMapbox.``Point mapbox chart of canadian cities`` + ); + testCase "Point mapbox layout" ( fun () -> + "var layout = {\"mapbox\":{\"style\":\"open-street-map\",\"center\":{\"lon\":-104.6,\"lat\":50.45}}};" + |> chartGeneratedContains PointMapbox.``Point mapbox chart of canadian cities`` + ); + ] + ] +module LineMapbox = + [] + let ``LineMapbox chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "LineMapbox" [ + testCase "Flights mapbox data" ( fun () -> + """var data = [{"type":"scattermapbox","opacity":1.0,"mode":"lines","lat":[32.89595056,35.04022222],"lon":[-97.0372,-106.6091944],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.3738738738738739,"mode":"lines","lat":[41.979595,30.19453278],"lon":[-87.90446417,-97.66987194],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.36486486486486486,"mode":"lines","lat":[32.89595056,41.93887417],"lon":[-97.0372,-72.68322833],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.12612612612612611,"mode":"lines","lat":[18.43941667,41.93887417],"lon":[-66.00183333,-72.68322833],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.3783783783783784,"mode":"lines","lat":[32.89595056,33.56294306],"lon":[-97.0372,-86.75354972],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.12612612612612611,"mode":"lines","lat":[25.79325,36.12447667],"lon":[-80.29055556,-86.67818222],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.9504504504504504,"mode":"lines","lat":[32.89595056,42.3643475],"lon":[-97.0372,-71.00517917],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.8828828828828829,"mode":"lines","lat":[25.79325,42.3643475],"lon":[-80.29055556,-71.00517917],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.9684684684684685,"mode":"lines","lat":[41.979595,42.3643475],"lon":[-87.90446417,-71.00517917],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.12612612612612611,"mode":"lines","lat":[18.43941667,42.3643475],"lon":[-66.00183333,-71.00517917],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.0990990990990991,"mode":"lines","lat":[18.33730556,42.3643475],"lon":[-64.97336111,-71.00517917],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.25225225225225223,"mode":"lines","lat":[25.79325,39.17540167],"lon":[-80.29055556,-76.66819833],"cluster":{},"marker":{},"line":{"color":"red"}}];""" + |> chartGeneratedContains LineMapbox.``LineMapbox plot of feb. 2011 American Airline flights`` + ); + testCase "Flights mapbox layout" ( fun () -> + """var layout = {"mapbox":{"style":"open-street-map","center":{"lon":-97.0372,"lat":32.8959}},"showlegend":false,"margin":{"l":0,"r":0,"t":50,"b":0},"title":{"text":"Feb. 2011 American Airline flights"}};""" + |> chartGeneratedContains LineMapbox.``LineMapbox plot of feb. 2011 American Airline flights`` + ); + ] + ] + +module BubbleMapbox = + [] + let ``BubbleMapbox chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "BubbleMapbox" [ + ] + ] + +module ChoroplethMapbox = + [] + let ``ChoroplethMapbox chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "ChoroplethMapbox" [ + ] + ] + +module DensityMapbox = + [] + let ``DensityMapbox chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartMap" [ + testList "DensityMapbox" [ + testCase "Density Mapbox data" ( fun () -> + """var data = [{"type":"densitymapbox","z":[6.0,5.8,6.2,5.8,5.8,6.7,5.9,6.0,6.0,5.8,5.9,8.2,5.5,5.6,6.0,6.1,8.7,6.0,5.7,5.8,5.9,5.9,5.7,5.7,5.7,5.6,7.3,6.5,5.6,6.4,5.8,5.8,5.8,5.7,5.7,6.3,5.7,6.0,5.6,6.4,6.2,5.6,5.7,5.7,6.3,5.8,5.7,5.7,5.8,5.9,5.6,6.0,5.8,5.8,5.9,5.7,5.7,5.6,5.6],"radius":8,"lat":[19.246,1.863,-20.579,-59.076,11.938,-13.405,27.357,-13.309,-56.452,-24.563,-6.807,-2.608,54.636,-18.697,37.523,-51.84,51.251000000000005,51.639,52.528,51.626000000000005,51.037,51.73,51.775,52.611,51.831,51.948,51.443000000000005,52.773,51.772,52.975,52.99,51.536,13.245,51.812,51.762,52.438,51.946000000000005,51.738,51.486999999999995,53.008,52.184,52.076,51.744,52.056999999999995,53.191,51.447,51.258,52.031000000000006,51.294,55.223,-18.718,52.815,52.188,51.00899999999999,3.026,38.908,51.694,21.526999999999997,25.011],"lon":[145.616,127.352,-173.972,-23.557,126.427,166.62900000000002,87.867,166.21200000000002,-27.043000000000003,178.487,108.988,125.952,161.703,-177.864,73.251,139.741,178.715,175.055,172.007,175.74599999999998,177.84799999999998,173.975,173.058,172.588,174.368,173.96900000000002,179.605,171.97400000000002,174.696,171.09099999999998,170.87400000000002,175.045,-44.922,174.206,174.84099999999998,174.321,173.84,174.56599999999997,176.558,-162.00799999999998,175.505,172.918,175.213,174.116,-161.859,176.46900000000002,173.393,175.41099999999997,179.092,165.426,169.386,171.90400000000002,172.752,179.325,125.951,142.095,176.446,143.08100000000002,94.186],"colorscale":"Viridis"}];""" + |> chartGeneratedContains DensityMapbox.``Density mapbox chart of earthquakes`` + ); + testCase "Density Mapbox layout" ( fun () -> + "var layout = {\"mapbox\":{\"style\":\"stamen-terrain\",\"center\":{\"lon\":60.0,\"lat\":30.0}}};" + |> chartGeneratedContains DensityMapbox.``Density mapbox chart of earthquakes`` + ); + ] + ] diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/ChartPolar.fs b/tests/CoreTests/CoreTests/HTMLCodegen/ChartPolar.fs new file mode 100644 index 000000000..16eb34a5f --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/ChartPolar.fs @@ -0,0 +1,87 @@ +module CoreTests.HTMLCodegen.ChartPolar + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open ChartPolarTestCharts + +module ScatterPolar = + [] + let ``ScatterPolar chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartPolar" [ + testList "ScatterPolar" [ + ] + ] + +module PointPolar = + [] + let ``PointPolar chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartPolar" [ + testList "PointPolar" [ + testCase "Polar Point data" ( fun () -> + """var data = [{"type":"scatterpolar","mode":"markers","r":[1,2,3,4,5,6,7],"theta":[0,45,90,135,200,320,184],"marker":{}}];""" + |> chartGeneratedContains PointPolar.``Simple polar point chart`` + ); + testCase "Polar Point layout" ( fun () -> + emptyLayout PointPolar.``Simple polar point chart`` + ); + ] + ] + +module LinePolar = + [] + let ``LinePolar chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartPolar" [ + testList "LinePolar" [ + testCase "Polar Line data" ( fun () -> + """var data = [{"type":"scatterpolar","mode":"lines","r":[1,2,3,4,5,6,7],"theta":[0,45,90,135,200,320,184],"marker":{},"line":{"color":"purple","dash":"dashdot"}}];""" + |> chartGeneratedContains LinePolar.``Simple polar line chart with line style`` + ); + testCase "Polar Line layout" ( fun () -> + emptyLayout LinePolar.``Simple polar line chart with line style`` + ); + ] + ] + +module SplinePolar = + [] + let ``SplinePolar chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartPolar" [ + testList "SplinePolar" [ + testCase "Polar Spline data" ( fun () -> + """var data = [{"type":"scatterpolar","mode":"lines+markers+text","r":[1,2,3,4,5,6,7],"theta":[0,45,90,135,200,320,184],"text":["one","two","three","four","five","six","seven"],"textposition":"top center","marker":{},"line":{"shape":"spline"}}];""" + |> chartGeneratedContains SplinePolar.``styled polar spline chart`` + ); + testCase "Polar Spline layout" ( fun () -> + emptyLayout SplinePolar.``styled polar spline chart`` + ); + ] + ] + +module BubblePolar = + [] + let ``BubblePolar chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartPolar" [ + testList "BubblePolar" [ + ] + ] + +module BarPolar = + [] + let ``BarPolar chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartPolar" [ + testList "BarPolar" [ + testCase "Polar Bar data" ( fun () -> + """var data = [{"type":"barpolar","name":"11-14 m/s","r":[77.5,72.5,70.0,45.0,22.5,42.5,40.0,62.5],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"+"}}},{"type":"barpolar","name":"8-11 m/s","r":[57.5,50.0,45.0,35.0,20.0,22.5,37.5,55.0],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"x"}}},{"type":"barpolar","name":"5-8 m/s","r":[40.0,30.0,30.0,35.0,7.5,7.5,32.5,40.0],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"|"}}},{"type":"barpolar","name":"< 5 m/s","r":[20.0,7.5,15.0,22.5,2.5,2.5,12.5,22.5],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"-"}}}];""" + |> chartGeneratedContains BarPolar.``Styled windrose chart`` + ); + testCase "Polar Bar layout" ( fun () -> + """var layout = {"polar":{"angularaxis":{"categoryorder":"array","categoryarray":["East","N-E","North","N-W","West","S-W","South","S-E"]}}};""" + |> chartGeneratedContains BarPolar.``Styled windrose chart`` + ); + ] + ] \ No newline at end of file diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/ChartSmith.fs b/tests/CoreTests/CoreTests/HTMLCodegen/ChartSmith.fs new file mode 100644 index 000000000..f563d376c --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/ChartSmith.fs @@ -0,0 +1,71 @@ +module CoreTests.HTMLCodegen.ChartSmith + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open ChartSmithTestCharts + +module ScatterSmith = + [] + let ``ScatterSmith chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartSmith" [ + testList "ScatterSmith" [ + testCase "Scatter data" ( fun () -> + """var data = [{"type":"scattersmith","mode":"lines+markers+text","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"text":["Pretty","Cool","Plot","Huh?"],"textposition":"top center","marker":{},"line":{}}];""" + |> chartGeneratedContains ScatterSmith.``Simple smith scatter chart`` + ) + testCase "Scatter layout" ( fun () -> + emptyLayout ScatterSmith.``Simple smith scatter chart`` + ) + ] + ] + + +module PointSmith = + [] + let ``PointSmith chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartSmith" [ + testList "PointSmith" [ + testCase "Point data" ( fun () -> + """var data = [{"type":"scattersmith","mode":"markers","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"marker":{},"line":{}}];""" + |> chartGeneratedContains PointSmith.``Simple smith point chart`` + ) + testCase "Point layout" ( fun () -> + emptyLayout PointSmith.``Simple smith point chart`` + ) + ] + ] + +module LineSmith = + [] + let ``LineSmith chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartSmith" [ + testList "LineSmith" [ + testCase "Line data" ( fun () -> + """var data = [{"type":"scattersmith","mode":"lines","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"marker":{},"line":{"color":"rgba(128, 0, 128, 1.0)","dash":"dashdot"}}];""" + |> chartGeneratedContains LineSmith.``Simple smith line chart`` + ) + testCase "Line layout" ( fun () -> + emptyLayout LineSmith.``Simple smith line chart`` + ) + ] + ] + +module BubbleSmith = + [] + let ``BubbleSmith chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartSmith" [ + testList "BubbleSmith" [ + testCase "Bubble data" ( fun () -> + """var data = [{"type":"scattersmith","mode":"markers+text","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"text":["one","two","three","four","five","six","seven"],"textposition":"top center","marker":{"size":[10,20,30,40]},"line":{}}];""" + |> chartGeneratedContains BubbleSmith.``Simple smith bubble chart`` + ) + testCase "Bubble layout" ( fun () -> + emptyLayout BubbleSmith.``Simple smith bubble chart`` + ) + ] + ] \ No newline at end of file diff --git a/tests/CoreTests/CoreTests/HTMLCodegen/ChartTernary.fs b/tests/CoreTests/CoreTests/HTMLCodegen/ChartTernary.fs new file mode 100644 index 000000000..03c6292e2 --- /dev/null +++ b/tests/CoreTests/CoreTests/HTMLCodegen/ChartTernary.fs @@ -0,0 +1,58 @@ +module CoreTests.HTMLCodegen.ChartTernary + +open Expecto +open Plotly.NET +open Plotly.NET.LayoutObjects +open Plotly.NET.TraceObjects +open Plotly.NET.GenericChart + +open TestUtils.HtmlCodegen +open ChartTernaryTestCharts + +module ScatterTernary = + [] + let ``ScatterTernary chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartTernary" [ + testList "ScatterTernary" [ + ] + ] + +module PointTernary = + [] + let ``PointTernary chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartTernary" [ + testList "PointTernary" [ + testCase "Point data" ( fun () -> + """var data = [{"type":"scatterternary","mode":"markers","a":[1],"b":[2],"c":[3],"marker":{},"line":{}}];""" + |> chartGeneratedContains PointTernary.``Styled ternary point chart`` + ) + testCase "Point layout" ( fun () -> + """var layout = {"ternary":{"aaxis":{"color":"rgba(153, 50, 204, 1.0)","title":{"text":"A"}},"baxis":{"color":"rgba(139, 0, 0, 1.0)","title":{"text":"B"}},"caxis":{"color":"rgba(0, 139, 139, 1.0)","title":{"text":"C"}}}};""" + |> chartGeneratedContains PointTernary.``Styled ternary point chart`` + ) + ] + ] + +module LineTernary = + [] + let ``LineTernary chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartTernary" [ + testList "LineTernary" [ + testCase "Line data" ( fun () -> + """var data = [{"type":"scatterternary","mode":"lines+markers","a":[10,20,30,40,50,60,70,80],"b":[80,70,60,50,40,30,20,10],"marker":{},"line":{"dash":"dashdot"},"sum":100}];""" + |> chartGeneratedContains LineTernary.``Styled ternary line chart`` + ) + testCase "Line layout" ( fun () -> + """var layout = {"ternary":{"aaxis":{"color":"rgba(153, 50, 204, 1.0)"},"baxis":{"color":"rgba(139, 0, 0, 1.0)"},"caxis":{"color":"rgba(0, 139, 139, 1.0)"}}};""" + |> chartGeneratedContains LineTernary.``Styled ternary line chart`` + ) + ] + ] + +module BubbleTernary = + [] + let ``BubbleTernary chart HTML codegeneration tests`` = + testList "HTMLCodegen.ChartTernary" [ + testList "BubbleTernary" [ + ] + ] diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/MulticategoryData.fs b/tests/CoreTests/CoreTests/HTMLCodegen/MulticategoryData.fs similarity index 100% rename from tests/Plotly.NET.Tests/HtmlCodegen/MulticategoryData.fs rename to tests/CoreTests/CoreTests/HTMLCodegen/MulticategoryData.fs diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/SimpleTests.fs b/tests/CoreTests/CoreTests/HTMLCodegen/SimpleTests.fs similarity index 54% rename from tests/Plotly.NET.Tests/HtmlCodegen/SimpleTests.fs rename to tests/CoreTests/CoreTests/HTMLCodegen/SimpleTests.fs index 7c932c92c..813491ed8 100644 --- a/tests/Plotly.NET.Tests/HtmlCodegen/SimpleTests.fs +++ b/tests/CoreTests/CoreTests/HTMLCodegen/SimpleTests.fs @@ -7,91 +7,55 @@ open Plotly.NET.TraceObjects open Plotly.NET.GenericChart open TestUtils.HtmlCodegen - -let simpleChart = - let xData = [0. .. 10.] - let yData = [0. .. 10.] - Chart.Point(x = xData, y = yData, UseDefaults = false) - |> Chart.withTitle "Hello world!" - |> Chart.withXAxisStyle (TitleText = "xAxis", ShowGrid=false) - |> Chart.withYAxisStyle (TitleText = "yAxis", ShowGrid=false) +open Chart2DTestCharts [] let ``Html layout tests`` = testList "SimpleTests.Simple tests" [ testCase "Expecting plotly js script reference in generated html document" ( fun () -> $"""https://cdn.plot.ly/plotly-{Globals.PLOTLYJS_VERSION}.min""" - |> substringIsInChart simpleChart toEmbeddedHTML + |> substringIsInChart Chart2DTestCharts.Point.``Point chart with axis labels and title`` toEmbeddedHTML ); testCase "Expecting data" ( fun () -> """var data = [{"type":"scatter","mode":"markers","x":[0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"marker":{},"line":{}}];""" - |> chartGeneratedContains simpleChart + |> chartGeneratedContains Chart2DTestCharts.Point.``Point chart with axis labels and title`` ); testCase "Expecting layout info" (fun () -> """var layout = {"title":{"text":"Hello world!"},"xaxis":{"title":{"text":"xAxis"},"showgrid":false},"yaxis":{"title":{"text":"yAxis"},"showgrid":false}};""" - |> chartGeneratedContains simpleChart + |> chartGeneratedContains Chart2DTestCharts.Point.``Point chart with axis labels and title`` ); testCase "Expecting html tags in embedded page only" (fun () -> [""; ""; ""; ""; ""; ""; ""] - |> substringListIsInChart simpleChart toEmbeddedHTML + |> substringListIsInChart Chart2DTestCharts.Point.``Point chart with axis labels and title`` toEmbeddedHTML ); testCase "Expecting some html tags in both embedded and not embedded" (fun () -> [""] - |> chartGeneratedContainsList simpleChart + |> chartGeneratedContainsList Chart2DTestCharts.Point.``Point chart with axis labels and title`` ); testCase "Passing args to the function" ( fun () -> "data, layout, config);" - |> chartGeneratedContains simpleChart + |> chartGeneratedContains Chart2DTestCharts.Point.``Point chart with axis labels and title`` ) ] -let fullPlotlyJSChart = - let xData = [0. .. 10.] - let yData = [0. .. 10.] - Chart.Point(xData, yData, UseDefaults = false) - |> Chart.withDisplayOptions( - DisplayOptions.init( - PlotlyJSReference = Full - ) - ) - -let cdnPlotlyJSChart = - let xData = [0. .. 10.] - let yData = [0. .. 10.] - Chart.Point(xData, yData, UseDefaults = false) - |> Chart.withDisplayOptions( - DisplayOptions.init( - PlotlyJSReference = CDN $"https://cdn.plot.ly/plotly-2.0.0.min" - ) - ) - -let requirePlotlyJSChart = - let xData = [0. .. 10.] - let yData = [0. .. 10.] - Chart.Point(xData, yData, UseDefaults = false) - |> Chart.withDisplayOptions( - DisplayOptions.init( - PlotlyJSReference = Require $"https://cdn.plot.ly/plotly-{Globals.PLOTLYJS_VERSION}.min" - ) - ) [] let ``plotlyjs reference tests`` = testList "SimpleTests.plotlyjs reference" [ testCase "full reference" (fun _ -> getFullPlotlyJS() - |> substringIsInChart fullPlotlyJSChart toEmbeddedHTML + |> substringIsInChart Chart2DTestCharts.Point.``Point chart with full plotly.js reference`` toEmbeddedHTML ) testCase "cdn reference" (fun _ -> $"""https://cdn.plot.ly/plotly-2.0.0.min""" - |> substringIsInChart cdnPlotlyJSChart toEmbeddedHTML + |> substringIsInChart Chart2DTestCharts.Point.``Point chart with plotly.js cdn reference`` toEmbeddedHTML ) testCase "require: Expecting cloudflare link" (fun () -> """https://cdnjs.cloudflare.com/ajax/libs/require.js""" - |> chartGeneratedContains requirePlotlyJSChart + |> chartGeneratedContains Chart2DTestCharts.Point.``Point chart referencing plotly.js using require.js`` ); testCase "require: Expecting require config" (fun () -> (sprintf """var fsharpPlotlyRequire = requirejs.config({context:'fsharp-plotly',paths:{plotly:'https://cdn.plot.ly/plotly-%s.min'}}) || require;""" Globals.PLOTLYJS_VERSION) - |> chartGeneratedContains requirePlotlyJSChart + |> chartGeneratedContains Chart2DTestCharts.Point.``Point chart referencing plotly.js using require.js`` ); ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/LayoutObjects/LinearAxis.fs b/tests/CoreTests/CoreTests/LayoutObjects/LinearAxis.fs similarity index 100% rename from tests/Plotly.NET.Tests/LayoutObjects/LinearAxis.fs rename to tests/CoreTests/CoreTests/LayoutObjects/LinearAxis.fs diff --git a/tests/Plotly.NET.Tests/LayoutObjects/Slider.fs b/tests/CoreTests/CoreTests/LayoutObjects/Slider.fs similarity index 100% rename from tests/Plotly.NET.Tests/LayoutObjects/Slider.fs rename to tests/CoreTests/CoreTests/LayoutObjects/Slider.fs diff --git a/tests/Plotly.NET.Tests/Main.fs b/tests/CoreTests/CoreTests/Main.fs similarity index 77% rename from tests/Plotly.NET.Tests/Main.fs rename to tests/CoreTests/CoreTests/Main.fs index 1f34e91d4..a3da167a0 100644 --- a/tests/Plotly.NET.Tests/Main.fs +++ b/tests/CoreTests/CoreTests/Main.fs @@ -1,6 +1,7 @@ -module Plotly.NET.Tests +module Main open Expecto [] let main argv = Tests.runTestsInAssemblyWithCLIArgs [] argv + \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/Traces/TraceID.fs b/tests/CoreTests/CoreTests/Traces/TraceID.fs similarity index 100% rename from tests/Plotly.NET.Tests/Traces/TraceID.fs rename to tests/CoreTests/CoreTests/Traces/TraceID.fs diff --git a/tests/Plotly.NET.Tests/Traces/TraceStaticMembers.fs b/tests/CoreTests/CoreTests/Traces/TraceStaticMembers.fs similarity index 100% rename from tests/Plotly.NET.Tests/Traces/TraceStaticMembers.fs rename to tests/CoreTests/CoreTests/Traces/TraceStaticMembers.fs diff --git a/tests/Plotly.NET.Tests/Traces/TraceStyle.fs b/tests/CoreTests/CoreTests/Traces/TraceStyle.fs similarity index 100% rename from tests/Plotly.NET.Tests/Traces/TraceStyle.fs rename to tests/CoreTests/CoreTests/Traces/TraceStyle.fs diff --git a/tests/Plotly.NET.Tests.StrongName/Program.fs b/tests/CoreTests/StrongNameTests/Program.fs similarity index 100% rename from tests/Plotly.NET.Tests.StrongName/Program.fs rename to tests/CoreTests/StrongNameTests/Program.fs diff --git a/tests/Plotly.NET.Tests.StrongName/Plotly.NET.Tests.StrongName.fsproj b/tests/CoreTests/StrongNameTests/StrongNameTests.fsproj similarity index 84% rename from tests/Plotly.NET.Tests.StrongName/Plotly.NET.Tests.StrongName.fsproj rename to tests/CoreTests/StrongNameTests/StrongNameTests.fsproj index 494cfc88d..4d25be81d 100644 --- a/tests/Plotly.NET.Tests.StrongName/Plotly.NET.Tests.StrongName.fsproj +++ b/tests/CoreTests/StrongNameTests/StrongNameTests.fsproj @@ -1,4 +1,4 @@ - + net48 @@ -7,7 +7,7 @@ false true true - ../../key.snk + ../../../key.snk @@ -29,7 +29,7 @@ - + diff --git a/tests/Plotly.NET.Tests.StrongName/Tests.fs b/tests/CoreTests/StrongNameTests/Tests.fs similarity index 100% rename from tests/Plotly.NET.Tests.StrongName/Tests.fs rename to tests/CoreTests/StrongNameTests/Tests.fs diff --git a/tests/Plotly.NET.CSharp.Tests/Plotly.NET.CSharp.Tests.csproj b/tests/ExtensionLibsTests/CSharpTests/CSharpTests.csproj similarity index 91% rename from tests/Plotly.NET.CSharp.Tests/Plotly.NET.CSharp.Tests.csproj rename to tests/ExtensionLibsTests/CSharpTests/CSharpTests.csproj index bfe679ebe..74d9efef1 100644 --- a/tests/Plotly.NET.CSharp.Tests/Plotly.NET.CSharp.Tests.csproj +++ b/tests/ExtensionLibsTests/CSharpTests/CSharpTests.csproj @@ -25,7 +25,7 @@ - + diff --git a/tests/Plotly.NET.CSharp.Tests/ExtensionMethodsTests.cs b/tests/ExtensionLibsTests/CSharpTests/ExtensionMethodsTests.cs similarity index 100% rename from tests/Plotly.NET.CSharp.Tests/ExtensionMethodsTests.cs rename to tests/ExtensionLibsTests/CSharpTests/ExtensionMethodsTests.cs diff --git a/tests/Plotly.NET.CSharp.Tests/TestUtils.cs b/tests/ExtensionLibsTests/CSharpTests/TestUtils.cs similarity index 100% rename from tests/Plotly.NET.CSharp.Tests/TestUtils.cs rename to tests/ExtensionLibsTests/CSharpTests/TestUtils.cs diff --git a/tests/Plotly.NET.CSharp.Tests/htmlcodegen/SimpleTests.cs b/tests/ExtensionLibsTests/CSharpTests/htmlcodegen/SimpleTests.cs similarity index 100% rename from tests/Plotly.NET.CSharp.Tests/htmlcodegen/SimpleTests.cs rename to tests/ExtensionLibsTests/CSharpTests/htmlcodegen/SimpleTests.cs diff --git a/tests/Plotly.NET.ImageExport.Tests/ImageExport.fs b/tests/ExtensionLibsTests/ImageExportTests/ImageExport.fs similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/ImageExport.fs rename to tests/ExtensionLibsTests/ImageExportTests/ImageExport.fs diff --git a/tests/Plotly.NET.ImageExport.Tests/Plotly.NET.ImageExport.Tests.fsproj b/tests/ExtensionLibsTests/ImageExportTests/ImageExportTests.fsproj similarity index 88% rename from tests/Plotly.NET.ImageExport.Tests/Plotly.NET.ImageExport.Tests.fsproj rename to tests/ExtensionLibsTests/ImageExportTests/ImageExportTests.fsproj index aaa994765..0c8df24aa 100644 --- a/tests/Plotly.NET.ImageExport.Tests/Plotly.NET.ImageExport.Tests.fsproj +++ b/tests/ExtensionLibsTests/ImageExportTests/ImageExportTests.fsproj @@ -21,6 +21,6 @@ - + diff --git a/tests/Plotly.NET.ImageExport.Tests/Main.fs b/tests/ExtensionLibsTests/ImageExportTests/Main.fs similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/Main.fs rename to tests/ExtensionLibsTests/ImageExportTests/Main.fs diff --git a/tests/Plotly.NET.ImageExport.Tests/data/linuxTestBase64JPG.txt b/tests/ExtensionLibsTests/ImageExportTests/data/linuxTestBase64JPG.txt similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/data/linuxTestBase64JPG.txt rename to tests/ExtensionLibsTests/ImageExportTests/data/linuxTestBase64JPG.txt diff --git a/tests/Plotly.NET.ImageExport.Tests/data/linuxTestBase64PNG.txt b/tests/ExtensionLibsTests/ImageExportTests/data/linuxTestBase64PNG.txt similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/data/linuxTestBase64PNG.txt rename to tests/ExtensionLibsTests/ImageExportTests/data/linuxTestBase64PNG.txt diff --git a/tests/Plotly.NET.ImageExport.Tests/data/testPNG.png b/tests/ExtensionLibsTests/ImageExportTests/data/testPNG.png similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/data/testPNG.png rename to tests/ExtensionLibsTests/ImageExportTests/data/testPNG.png diff --git a/tests/Plotly.NET.ImageExport.Tests/data/testSVGURI.txt b/tests/ExtensionLibsTests/ImageExportTests/data/testSVGURI.txt similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/data/testSVGURI.txt rename to tests/ExtensionLibsTests/ImageExportTests/data/testSVGURI.txt diff --git a/tests/Plotly.NET.ImageExport.Tests/data/winTestBase64JPG.txt b/tests/ExtensionLibsTests/ImageExportTests/data/winTestBase64JPG.txt similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/data/winTestBase64JPG.txt rename to tests/ExtensionLibsTests/ImageExportTests/data/winTestBase64JPG.txt diff --git a/tests/Plotly.NET.ImageExport.Tests/data/winTestBase64PNG.txt b/tests/ExtensionLibsTests/ImageExportTests/data/winTestBase64PNG.txt similarity index 100% rename from tests/Plotly.NET.ImageExport.Tests/data/winTestBase64PNG.txt rename to tests/ExtensionLibsTests/ImageExportTests/data/winTestBase64PNG.txt diff --git a/tests/JSTests/.gitkeep b/tests/JSTests/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/CarpetCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/CarpetCharts.fs deleted file mode 100644 index 37ea1adc9..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/CarpetCharts.fs +++ /dev/null @@ -1,146 +0,0 @@ -module Tests.CarpetCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open System - -open TestUtils.HtmlCodegen - -let a = [4.; 4.; 4.; 4.5; 4.5; 4.5; 5.; 5.; 5.; 6.; 6.; 6.] -let b = [1.; 2.; 3.; 1.; 2.; 3.; 1.; 2.; 3.; 1.; 2.; 3.] -let y = [2.; 3.5; 4.; 3.; 4.5; 5.; 5.5; 6.5; 7.5; 8.; 8.5; 10.] - -let carpets = - [ - Chart.Carpet(carpetId = "carpet1",A = a, B = b, Y = y, UseDefaults = false) - Chart.Carpet(carpetId = "carpet2",A = (a |> List.rev) , B = (b |> List.rev), Y = (y |> List.map (fun x -> x + 10.)), UseDefaults = false) - Chart.Carpet(carpetId = "carpet3",A = a, B = b, Y = (y |> List.map (fun x -> x + 20.)), UseDefaults = false) - Chart.Carpet(carpetId = "carpet4",A = (a |> List.rev) , B = (b |> List.rev), Y = (y |> List.map (fun x -> x + 30.)), UseDefaults = false) - Chart.Carpet(carpetId = "carpet5",A = a, B = b, Y = (y |> List.map (fun x -> x + 40.)), UseDefaults = false) - ] - -let aData = [4.; 5.; 5.; 6.] -let bData = [1.; 1.; 2.; 3.] -let sizes = [5; 10; 15; 20] - - -let carpetCharts = - [ - Chart.ScatterCarpet( - a = aData,b = bData, - mode = StyleParam.Mode.Lines_Markers, - carpetAnchorId = "carpet1", - Name = "Scatter", - MultiMarkerSymbol =[ - StyleParam.MarkerSymbol.ArrowDown - StyleParam.MarkerSymbol.TriangleNW - StyleParam.MarkerSymbol.DiamondX - StyleParam.MarkerSymbol.Hexagon2 - ], - MarkerColor = Color.fromColors ([Red; Blue; Green; Yellow] |> List.map Color.fromKeyword), - UseDefaults = false - ) - Chart.PointCarpet(a = aData, b = bData, carpetAnchorId = "carpet2",Name = "Point", UseDefaults = false) - Chart.LineCarpet(a = aData, b = bData, carpetAnchorId = "carpet3",Name = "Line", UseDefaults = false) - Chart.SplineCarpet(a = aData, b = bData, carpetAnchorId = "carpet4",Name = "Spline", UseDefaults = false) - Chart.BubbleCarpet(absizes = (Seq.zip3 aData bData sizes), carpetAnchorId = "carpet5",Name = "Bubble", UseDefaults = false) - ] - -let scatter = Chart.combine [carpets.[0]; carpetCharts.[0]] -let point = Chart.combine [carpets.[1]; carpetCharts.[1]] -let line = Chart.combine [carpets.[2]; carpetCharts.[2]] -let spline = Chart.combine [carpets.[3]; carpetCharts.[3]] -let bubble = Chart.combine [carpets.[4]; carpetCharts.[4]] - - -[] -let ``ScatterCarpet and derived Charts`` = - testList "CarpetCharts.ScatterCarpet and derived Charts" [ - testCase "ScatterCarpet data" ( fun () -> - """var data = [{"type":"carpet","y":[2.0,3.5,4.0,3.0,4.5,5.0,5.5,6.5,7.5,8.0,8.5,10.0],"a":[4.0,4.0,4.0,4.5,4.5,4.5,5.0,5.0,5.0,6.0,6.0,6.0],"b":[1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0],"carpet":"carpet1"},{"type":"scattercarpet","name":"Scatter","mode":"lines+markers","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{"color":["rgba(255, 0, 0, 1.0)","rgba(0, 0, 255, 1.0)","rgba(0, 128, 0, 1.0)","rgba(255, 255, 0, 1.0)"],"symbol":["46","12","32","15"]},"line":{},"carpet":"carpet1"}];""" - |> chartGeneratedContains scatter - ); - testCase "ScatterCarpet layout" ( fun () -> - emptyLayout scatter - ); - testCase "PointCarpet data" ( fun () -> - """var data = [{"type":"carpet","y":[12.0,13.5,14.0,13.0,14.5,15.0,15.5,16.5,17.5,18.0,18.5,20.0],"a":[6.0,6.0,6.0,5.0,5.0,5.0,4.5,4.5,4.5,4.0,4.0,4.0],"b":[3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0],"carpet":"carpet2"},{"type":"scattercarpet","name":"Point","mode":"markers","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{},"line":{},"carpet":"carpet2"}];""" - |> chartGeneratedContains point - ); - testCase "PointCarpet layout" ( fun () -> - emptyLayout point - ); - testCase "LineCarpet data" ( fun () -> - """var data = [{"type":"carpet","y":[22.0,23.5,24.0,23.0,24.5,25.0,25.5,26.5,27.5,28.0,28.5,30.0],"a":[4.0,4.0,4.0,4.5,4.5,4.5,5.0,5.0,5.0,6.0,6.0,6.0],"b":[1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0],"carpet":"carpet3"},{"type":"scattercarpet","name":"Line","mode":"lines","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{},"line":{},"carpet":"carpet3"}];""" - |> chartGeneratedContains line - ); - testCase "LineCarpet layout" ( fun () -> - emptyLayout line - ); - testCase "SplineCarpet data" ( fun () -> - """var data = [{"type":"carpet","y":[32.0,33.5,34.0,33.0,34.5,35.0,35.5,36.5,37.5,38.0,38.5,40.0],"a":[6.0,6.0,6.0,5.0,5.0,5.0,4.5,4.5,4.5,4.0,4.0,4.0],"b":[3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0,3.0,2.0,1.0],"carpet":"carpet4"},{"type":"scattercarpet","name":"Spline","mode":"lines","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{},"line":{"shape":"spline"},"carpet":"carpet4"}];""" - |> chartGeneratedContains spline - ); - testCase "SplineCarpet layout" ( fun () -> - emptyLayout spline - ); - testCase "BubbleCarpet data" ( fun () -> - """var data = [{"type":"carpet","y":[42.0,43.5,44.0,43.0,44.5,45.0,45.5,46.5,47.5,48.0,48.5,50.0],"a":[4.0,4.0,4.0,4.5,4.5,4.5,5.0,5.0,5.0,6.0,6.0,6.0],"b":[1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0,1.0,2.0,3.0],"carpet":"carpet5"},{"type":"scattercarpet","name":"Bubble","mode":"markers","a":[4.0,5.0,5.0,6.0],"b":[1.0,1.0,2.0,3.0],"marker":{"size":[5,10,15,20]},"line":{},"carpet":"carpet5"}];""" - |> chartGeneratedContains bubble - ); - testCase "BubbleCarpet layout" ( fun () -> - emptyLayout bubble - ); - - ] - - -let contour = - [ - Chart.Carpet( - carpetId = "contour", - A = [0.; 1.; 2.; 3.; 0.; 1.; 2.; 3.; 0.; 1.; 2.; 3.], - B = [4.; 4.; 4.; 4.; 5.; 5.; 5.; 5.; 6.; 6.; 6.; 6.], - X = [2.; 3.; 4.; 5.; 2.2; 3.1; 4.1; 5.1; 1.5; 2.5; 3.5; 4.5], - Y = [1.; 1.4; 1.6; 1.75; 2.; 2.5; 2.7; 2.75; 3.; 3.5; 3.7; 3.75], - AAxis = LinearAxis.initCarpet( - TickPrefix = "a = ", - Smoothing = 0., - MinorGridCount = 9, - AxisType = StyleParam.AxisType.Linear - ), - BAxis = LinearAxis.initCarpet( - TickPrefix = "b = ", - Smoothing = 0., - MinorGridCount = 9, - AxisType = StyleParam.AxisType.Linear - ), - UseDefaults = false, - Opacity = 0.75 - ) - Chart.ContourCarpet( - z = [1.; 1.96; 2.56; 3.0625; 4.; 5.0625; 1.; 7.5625; 9.; 12.25; 15.21; 14.0625], - carpetAnchorId = "contour", - A = [0; 1; 2; 3; 0; 1; 2; 3; 0; 1; 2; 3], - B = [4; 4; 4; 4; 5; 5; 5; 5; 6; 6; 6; 6], - UseDefaults = false, - ContourLineColor = Color.fromKeyword White, - ShowContourLabels = true - ) - ] - |> Chart.combine - -[] -let ``ContourCarpet Charts`` = - testList "CarpetCharts.ContourCarpet Charts" [ - testCase "ContourCarpet data" ( fun () -> - """var data = [{"type":"carpet","opacity":0.75,"x":[2.0,3.0,4.0,5.0,2.2,3.1,4.1,5.1,1.5,2.5,3.5,4.5],"y":[1.0,1.4,1.6,1.75,2.0,2.5,2.7,2.75,3.0,3.5,3.7,3.75],"a":[0.0,1.0,2.0,3.0,0.0,1.0,2.0,3.0,0.0,1.0,2.0,3.0],"b":[4.0,4.0,4.0,4.0,5.0,5.0,5.0,5.0,6.0,6.0,6.0,6.0],"aaxis":{"type":"linear","tickprefix":"a = ","minorgridcount":9,"smoothing":0.0},"baxis":{"type":"linear","tickprefix":"b = ","minorgridcount":9,"smoothing":0.0},"carpet":"contour"},{"type":"contourcarpet","z":[1.0,1.96,2.56,3.0625,4.0,5.0625,1.0,7.5625,9.0,12.25,15.21,14.0625],"a":[0,1,2,3,0,1,2,3,0,1,2,3],"b":[4,4,4,4,5,5,5,5,6,6,6,6],"line":{"color":"rgba(255, 255, 255, 1.0)"},"carpet":"contour","contours":{"showlabels":true}}];""" - |> chartGeneratedContains contour - ); - testCase "ScatterCarpet layout" ( fun () -> - emptyLayout contour - ); - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/CategoricalCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/CategoricalCharts.fs deleted file mode 100644 index 6340adb3c..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/CategoricalCharts.fs +++ /dev/null @@ -1,317 +0,0 @@ -module Tests.CategoricalCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open System - -open TestUtils.HtmlCodegen - -let parcats = - let dims = - [ - Dimension.initParallel(Values = ["Cat1";"Cat1";"Cat1";"Cat1";"Cat2";"Cat2";"Cat3"],Label="A") - Dimension.initParallel(Values = [0;1;0;1;0;0;0],Label="B",TickText=["YES";"NO"]) - ] - - Chart.ParallelCategories( - dimensions = dims, - LineColor=Color.fromColorScaleValues [0.;1.;0.;1.;0.;0.;0.], - LineColorScale = StyleParam.Colorscale.Blackbody, - UseDefaults = false - ) - - -let parcatsStyled = - let dims = - [ - Dimension.initParallel(Values = ["A";"A";"A";"B";"B";"B";"C";"D"],Label="Lvl1") - Dimension.initParallel(Values = ["AA";"AA";"AB";"AB";"AB";"AB";"AB";"AB"],Label="Lvl2") - Dimension.initParallel(Values = ["AAA";"AAB";"AAC";"AAC";"AAB";"AAB";"AAA";"AAA"],Label="Lvl3") - ] - - Chart.ParallelCategories( - dimensions = dims, - LineColor = Color.fromColorScaleValues [0; 1; 2; 2; 1; 1; 0; 0], // These values map to the last category axis, meaning [AAA => 0; AAB = 1; AAC => 2] - LineColorScale = StyleParam.Colorscale.Viridis, - BundleColors = false, - UseDefaults = false - ) - -[] -let ``Parallel categories charts`` = - testList "CategoricalCharts.Parallel categories charts" [ - testCase "Parallel categories data" ( fun () -> - """var data = [{"type":"parcats","dimensions":[{"label":"A","values":["Cat1","Cat1","Cat1","Cat1","Cat2","Cat2","Cat3"],"axis":{}},{"label":"B","values":[0,1,0,1,0,0,0],"ticktext":["YES","NO"],"axis":{}}],"line":{"color":[0.0,1.0,0.0,1.0,0.0,0.0,0.0],"colorscale":"Blackbody"}}];""" - |> chartGeneratedContains parcats - ); - testCase "Parallel categories layout" ( fun () -> - emptyLayout parcats - ); - testCase "Parallel categories styled data" ( fun () -> - """var data = [{"type":"parcats","dimensions":[{"label":"Lvl1","values":["A","A","A","B","B","B","C","D"],"axis":{}},{"label":"Lvl2","values":["AA","AA","AB","AB","AB","AB","AB","AB"],"axis":{}},{"label":"Lvl3","values":["AAA","AAB","AAC","AAC","AAB","AAB","AAA","AAA"],"axis":{}}],"line":{"color":[0,1,2,2,1,1,0,0],"colorscale":"Viridis"},"bundlecolors":false}];""" - |> chartGeneratedContains parcatsStyled - ); - testCase "Parallel categories styled layout" ( fun () -> - emptyLayout parcatsStyled - ); - ] - - -let parcoords = - let data = - [ - "A",[1.;4.;3.4;0.7;] - "B",[3.;1.5;1.7;2.3;] - "C",[2.;4.;3.1;5.] - "D",[4.;2.;2.;4.;] - ] - Chart.ParallelCoord(keyValues = data,LineColor=Color.fromString "blue", UseDefaults = false) - -let parcoordsStyled = - - let dims = - [ - Dimension.initParallel(Label = "1", Values = ([1;2;3;4] ), Range = StyleParam.Range.MinMax(0., 8.)) - Dimension.initParallel(Label = "2", Values = ([1;2;3;4] |> List.rev ), Range = StyleParam.Range.MinMax(0., 8.)) - Dimension.initParallel(Label = "3", Values = ([1;2;3;4] ), Range = StyleParam.Range.MinMax(0., 8.)) - Dimension.initParallel(Label = "4", Values = ([1;2;3;4] |> List.rev ), Range = StyleParam.Range.MinMax(0., 8.)) - ] - - let colors = - [1;2;3;4] - |> Color.fromColorScaleValues - - Chart.ParallelCoord( - dimensions = dims, - LineColorScale = StyleParam.Colorscale.Viridis, - LineColor = colors, - UseDefaults = false - ) - -[] -let ``Parallel coordinates charts`` = - testList "CategoricalCharts.Parallel coordinates charts" [ - testCase "Parallel coordinates data" ( fun () -> - """var data = [{"type":"parcoords","dimensions":[{"label":"A","values":[1.0,4.0,3.4,0.7],"axis":{}},{"label":"B","values":[3.0,1.5,1.7,2.3],"axis":{}},{"label":"C","values":[2.0,4.0,3.1,5.0],"axis":{}},{"label":"D","values":[4.0,2.0,2.0,4.0],"axis":{}}],"line":{"color":"blue"}}];""" - |> chartGeneratedContains parcoords - ); - testCase "Parallel coordinates layout" ( fun () -> - emptyLayout parcoords - ); - testCase "Parallel coordinates styled data" ( fun () -> - """var data = [{"type":"parcoords","dimensions":[{"label":"1","values":[1,2,3,4],"range":[0.0,8.0],"axis":{}},{"label":"2","values":[4,3,2,1],"range":[0.0,8.0],"axis":{}},{"label":"3","values":[1,2,3,4],"range":[0.0,8.0],"axis":{}},{"label":"4","values":[4,3,2,1],"range":[0.0,8.0],"axis":{}}],"line":{"color":[1,2,3,4],"colorscale":"Viridis"}}];""" - |> chartGeneratedContains parcoordsStyled - ); - testCase "Parallel coordinates styled layout" ( fun () -> - emptyLayout parcoordsStyled - ); - ] - - - -let styledSankey = - Chart.Sankey( - nodeLabels = ["A1"; "A2"; "B1"; "B2"; "C1"; "C2"; "D1"], - linkedNodeIds = [ - 0,2 - 0,3 - 1,3 - 2,4 - 3,4 - 3,5 - 4,6 - 5,6 - ], - NodeOutlineColor = Color.fromKeyword Black, - NodeOutlineWidth = 1., - linkValues = [8; 4; 2; 7; 3; 2; 5; 2], - LinkColor = Color.fromColors [ - Color.fromHex "#828BFB" - Color.fromHex "#828BFB" - Color.fromHex "#F27762" - Color.fromHex "#33D6AB" - Color.fromHex "#BC82FB" - Color.fromHex "#BC82FB" - Color.fromHex "#FFB47B" - Color.fromHex "#47DCF5" - ], - LinkOutlineColor = Color.fromKeyword Black, - LinkOutlineWidth = 1., - UseDefaults = false - ) - - -[] -let ``Sankey charts`` = - testList "CategoricalCharts.Sankey charts" [ - testCase "Sankey data" ( fun () -> - """var data = [{"type":"sankey","node":{"label":["A1","A2","B1","B2","C1","C2","D1"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":1.0}},"link":{"color":["rgba(130, 139, 251, 1.0)","rgba(130, 139, 251, 1.0)","rgba(242, 119, 98, 1.0)","rgba(51, 214, 171, 1.0)","rgba(188, 130, 251, 1.0)","rgba(188, 130, 251, 1.0)","rgba(255, 180, 123, 1.0)","rgba(71, 220, 245, 1.0)"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":1.0},"source":[0,0,1,2,3,3,4,5],"target":[2,3,3,4,4,5,6,6],"value":[8,4,2,7,3,2,5,2]}}];""" - |> chartGeneratedContains styledSankey - ) - testCase "Sankey layout" ( fun () -> - emptyLayout styledSankey - ) - ] - -let character = ["Eve"; "Cain"; "Seth"; "Enos"; "Noam"; "Abel"; "Awan"; "Enoch"; "Azura"] -let parent = [""; "Eve"; "Eve"; "Seth"; "Seth"; "Eve"; "Eve"; "Awan"; "Eve" ] - -let icicleChart = - Chart.Icicle( - labels = character, - parents = parent, - ShowSectionColorScale = true, - SectionColorScale = StyleParam.Colorscale.Viridis, - TilingOrientation = StyleParam.Orientation.Vertical, - TilingFlip = StyleParam.TilingFlip.Y, - PathBarEdgeShape = StyleParam.PathbarEdgeShape.BackSlash, - UseDefaults = false - ) - -let icicleStyled = - let labelsParents = [ - ("A",""), 20 - ("B",""), 1 - ("C",""), 2 - ("D",""), 3 - ("E",""), 4 - - ("AA","A"), 15 - ("AB","A"), 5 - - ("BA","B"), 1 - - ("AAA", "AA"), 10 - ("AAB", "AA"), 5 - ] - - Chart.Icicle( - labelsparents = (labelsParents |> Seq.map fst), - Values = (labelsParents |> Seq.map snd), - BranchValues = StyleParam.BranchValues.Total, // branch values are the total of their childrens values - SectionColorScale = StyleParam.Colorscale.Viridis, - ShowSectionColorScale = true, - SectionOutlineColor = Color.fromKeyword Black, - Tiling = IcicleTiling.init(Flip = StyleParam.TilingFlip.XY), - UseDefaults = false - ) - -[] -let ``Icicle charts`` = - testList "CategoricalCharts.Icicle charts" [ - testCase "Icicle data" ( fun () -> - """var data = [{"type":"icicle","parents":["","Eve","Eve","Seth","Seth","Eve","Eve","Awan","Eve"],"labels":["Eve","Cain","Seth","Enos","Noam","Abel","Awan","Enoch","Azura"],"marker":{"colorscale":"Viridis","line":{},"showscale":true},"tiling":{"flip":"y","orientation":"v"},"pathbar":{"edgeshape":"\\"}}];""" - |> chartGeneratedContains icicleChart - ) - testCase "Icicle layout" ( fun () -> - emptyLayout icicleChart - ) - testCase "Icicle styled data" ( fun () -> - """var data = [{"type":"icicle","parents":["","","","","","A","A","B","AA","AA"],"values":[20,1,2,3,4,15,5,1,10,5],"labels":["A","B","C","D","E","AA","AB","BA","AAA","AAB"],"marker":{"colorscale":"Viridis","line":{"color":"rgba(0, 0, 0, 1.0)"},"showscale":true},"branchvalues":"total","tiling":{"flip":"x+y"},"pathbar":{}}];""" - |> chartGeneratedContains icicleStyled - ) - testCase "Icicle styled layout" ( fun () -> - emptyLayout icicleStyled - ) - ] - - -let sunburstChart = - Chart.Sunburst( - labels = ["A";"B";"C";"D";"E"], - parents = ["";"";"B";"B";""], - Values=[5.;0.;3.;2.;3.], - MultiText=["At";"Bt";"Ct";"Dt";"Et"], - UseDefaults = false - ) - -let sunburstStyled = - let labelsParents = [ - ("A",""), 20 - ("B",""), 1 - ("C",""), 2 - ("D",""), 3 - ("E",""), 4 - - ("AA","A"), 15 - ("AB","A"), 5 - - ("BA","B"), 1 - - ("AAA", "AA"), 10 - ("AAB", "AA"), 5 - ] - - Chart.Sunburst( - labelsparents = (labelsParents |> Seq.map fst), - Values = (labelsParents |> Seq.map snd), - BranchValues = StyleParam.BranchValues.Total, // branch values are the total of their childrens values - SectionColorScale = StyleParam.Colorscale.Viridis, - ShowSectionColorScale = true, - SectionOutlineColor = Color.fromKeyword Black, - Rotation = 45, - UseDefaults = false - ) - -[] -let ``Sunburst charts`` = - testList "CategoricalCharts.Sunburst charts" [ - testCase "Sunburst data" ( fun () -> - """var data = [{"type":"sunburst","parents":["","","B","B",""],"values":[5.0,0.0,3.0,2.0,3.0],"labels":["A","B","C","D","E"],"text":["At","Bt","Ct","Dt","Et"],"marker":{"line":{}}}];""" - |> chartGeneratedContains sunburstChart - ); - testCase "Sunburst layout" ( fun () -> - emptyLayout sunburstChart - ); - testCase "Sunburst styled data" ( fun () -> - """var data = [{"type":"sunburst","parents":["","","","","","A","A","B","AA","AA"],"values":[20,1,2,3,4,15,5,1,10,5],"labels":["A","B","C","D","E","AA","AB","BA","AAA","AAB"],"marker":{"colorscale":"Viridis","line":{"color":"rgba(0, 0, 0, 1.0)"},"showscale":true},"branchvalues":"total","rotation":45}];""" - |> chartGeneratedContains sunburstStyled - ); - testCase "Sunburst styled layout" ( fun () -> - emptyLayout sunburstStyled - ); - ] - - -let treemapStyled = - let labelsParents = [ - ("A",""), 20 - ("B",""), 1 - ("C",""), 2 - ("D",""), 3 - ("E",""), 4 - - ("AA","A"), 15 - ("AB","A"), 5 - - ("BA","B"), 1 - - ("AAA", "AA"), 10 - ("AAB", "AA"), 5 - ] - - Chart.Treemap( - labelsparents = (labelsParents |> Seq.map fst), - Values = (labelsParents |> Seq.map snd), - BranchValues = StyleParam.BranchValues.Total, // branch values are the total of their childrens values - SectionColorScale = StyleParam.Colorscale.Viridis, - ShowSectionColorScale = true, - SectionOutlineColor = Color.fromKeyword Black, - Tiling = TreemapTiling.init(Packing = StyleParam.TreemapTilingPacking.SliceDice), - UseDefaults = false - ) - -[] -let ``Treemap charts`` = - testList "CategoricalCharts.Treemap charts" [ - testCase "Treemap styled data" ( fun () -> - """var data = [{"type":"treemap","parents":["","","","","","A","A","B","AA","AA"],"values":[20,1,2,3,4,15,5,1,10,5],"labels":["A","B","C","D","E","AA","AB","BA","AAA","AAB"],"marker":{"colorscale":"Viridis","line":{"color":"rgba(0, 0, 0, 1.0)"},"showscale":true},"branchvalues":"total","tiling":{"packing":"slice-dice"}}];""" - |> chartGeneratedContains treemapStyled - ); - testCase "Treemap styled layout" ( fun () -> - emptyLayout treemapStyled - ); - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/Charts3D.fs b/tests/Plotly.NET.Tests/HtmlCodegen/Charts3D.fs deleted file mode 100644 index 974f0fedb..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/Charts3D.fs +++ /dev/null @@ -1,333 +0,0 @@ -module Tests.Charts3D - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open Plotly.NET.LayoutObjects -open System - -open TestUtils.HtmlCodegen - -//---------------------- Generate linearly spaced vector ---------------------- -let linspace (min,max,n) = - if n <= 2 then failwithf "n needs to be larger then 2" - let bw = float (max - min) / (float n - 1.) - Array.init n (fun i -> min + (bw * float i)) - -//-------------------- Generate linearly spaced mesh grid --------------------- -let mgrid (min,max,n) = - - let data = linspace(min,max,n) - - let z = [|for i in 1 .. n do [|for i in 1 .. n do yield data|]|] - let x = [|for i in 1 .. n do [|for j in 1 .. n do yield [|for k in 1 .. n do yield data.[i-1]|]|]|] - let y = [|for i in 1 .. n do [|for j in 1 .. n do yield [|for k in 1 .. n do yield data.[j-1]|]|]|] - - x,y,z - -let scatterChart = - let x = [19; 26; 55;] - let y = [19; 26; 55;] - let z = [19; 26; 55;] - - Chart.Scatter3D(x = x,y = y,z = z, mode = StyleParam.Mode.Markers, UseDefaults = false) - |> Chart.withXAxisStyle("my x-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withYAxisStyle("my y-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withZAxisStyle("my z-axis") - |> Chart.withSize(800,800) - -[] -let ``3D Scatter charts`` = - testList "Charts3D.3D Scatter charts" [ - testCase "3D Scatter charts data" ( fun () -> - """var data = [{"type":"scatter3d","mode":"markers","x":[19,26,55],"y":[19,26,55],"z":[19,26,55],"marker":{},"line":{}}];""" - |> chartGeneratedContains scatterChart - ); - testCase "3D Scatter charts layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"my x-axis"}},"yaxis":{"title":{"text":"my y-axis"}},"zaxis":{"title":{"text":"my z-axis"}}},"width":800,"height":800};""" - |> chartGeneratedContains scatterChart - ); - ] - -let pointChart = - let x = [19; 26; 55;] - let y = [19; 26; 55;] - let z = [19; 26; 55;] - - Chart.Point3D(x = x,y = y,z = z, UseDefaults = false) - |> Chart.withXAxisStyle("my x-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withYAxisStyle("my y-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withZAxisStyle("my z-axis") - |> Chart.withSize(800,800) - -[] -let ``3D Point charts`` = - testList "Charts3D.3D Point charts" [ - testCase "3D Point charts data" ( fun () -> - """var data = [{"type":"scatter3d","mode":"markers","x":[19,26,55],"y":[19,26,55],"z":[19,26,55],"marker":{},"line":{}}];""" - |> chartGeneratedContains pointChart - ); - testCase "3D Point charts layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"my x-axis"}},"yaxis":{"title":{"text":"my y-axis"}},"zaxis":{"title":{"text":"my z-axis"}}},"width":800,"height":800};""" - |> chartGeneratedContains pointChart - ); - ] - -let lineChart = - let c = [0. .. 0.5 .. 15.] - - let x, y, z = - c - |> List.map (fun i -> - let i' = float i - let r = 10. * Math.Cos (i' / 10.) - (r * Math.Cos i', r * Math.Sin i', i') - |> fun (x,y,z) -> - Math.Round(x,3), - Math.Round(y,3), - Math.Round(z,3) - ) - |> List.unzip3 - - Chart.Line3D(x = x, y = y, z = z, ShowMarkers=true, UseDefaults = false) - |> Chart.withXAxisStyle("x-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withYAxisStyle("y-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withZAxisStyle("z-axis") - |> Chart.withSize(800, 800) - -[] -let ``Line charts`` = - testList "Charts3D.Line charts" [ - testCase "Line data" ( fun () -> - """var data = [{"type":"scatter3d","mode":"lines+markers","x":[10.0,8.765,5.376,0.699,-4.079,-7.762,-9.458,-8.797,-6.02,-1.898,2.489,6.042,7.925,7.774,5.766,2.536,-1.014,-3.973,-5.664,-5.8,-4.534,-2.366,0.02,1.974,3.058,3.146,2.427,1.303,0.232,-0.428,-0.537],"y":[0.0,4.788,8.373,9.863,8.912,5.799,1.348,-3.295,-6.971,-8.802,-8.415,-6.015,-2.306,1.713,5.025,6.863,6.893,5.27,2.562,-0.437,-2.939,-4.377,-4.536,-3.576,-1.944,-0.209,1.124,1.76,1.684,1.127,0.46],"z":[0.0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0,10.5,11.0,11.5,12.0,12.5,13.0,13.5,14.0,14.5,15.0],"marker":{},"line":{}}];""" - |> chartGeneratedContains lineChart - ); - testCase "Line layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"x-axis"}},"yaxis":{"title":{"text":"y-axis"}},"zaxis":{"title":{"text":"z-axis"}}},"width":800,"height":800};""" - |> chartGeneratedContains lineChart - ); - ] - -let bubbleChart = - Chart.Bubble3D( - xyz = [1,3,2; 6,5,4; 7,9,8], - sizes = [20; 40; 30], - MultiText = ["A"; "B"; "C"], - TextPosition = StyleParam.TextPosition.TopLeft, - UseDefaults = false - ) - |> Chart.withXAxisStyle("x-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withYAxisStyle("y-axis", Id=StyleParam.SubPlotId.Scene 1) - |> Chart.withZAxisStyle("z-axis") - - -[] -let ``Bubble charts`` = - testList "Charts3D.Bubble charts" [ - testCase "Bubble data" ( fun () -> - """var data = [{"type":"scatter3d","mode":"markers+text","x":[1,6,7],"y":[3,5,9],"z":[2,4,8],"text":["A","B","C"],"textposition":"top left","marker":{"size":[20,40,30]}}];""" - |> chartGeneratedContains bubbleChart - ); - testCase "Bubble layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}},"xaxis":{"title":{"text":"x-axis"}},"yaxis":{"title":{"text":"y-axis"}},"zaxis":{"title":{"text":"z-axis"}}}};""" - |> chartGeneratedContains bubbleChart - ); - ] - - -let firstSurfaceChart = - - //---------------------- Create example data ---------------------- - let size = 100 - let x = linspace(-2. * Math.PI, 2. * Math.PI, size) - let y = linspace(-2. * Math.PI, 2. * Math.PI, size) - - let f x y = - (5. * x / (x**2. + y**2. + 1.) ) - - let z = - Array.init size (fun i -> - Array.init size (fun j -> f x.[j] y.[i] ) - ) - - Chart.Surface(zData = z, UseDefaults = false) - - -let secondSurfaceChart = - let x' = [0.;2.5] - let y' = [0.;2.5] - let z' = [ - [1.;1.;]; // row wise (length x) - [1.;2.;]; - ] // column (length y) - - Chart.Surface(zData = z', X = x', Y = y', Opacity=0.5, Contours=Contours.initXyz(Show=true), UseDefaults = false) - -[] -let ``Surface charts`` = - testList "Charts3D.Surface charts" [ - testCase "First surface data" ( fun () -> - // the data generated by this chart generates a - // line of code which is 200 Kb long, unreasonably - // huge for a test. - [ - "var data = [{\"type\":\"surface\",\"z\":[[0.3929110807586562,0.39272903726671055,0.3923748718856843,0.3918384347714509,0.39110921172503726,0.39017633288191317,0.38902858492457726" - "-0.3755066766683234,-0.38085145315419006,-0.38575670319102695,-0.3902383753762268,-0.3943127362115111,-0.3979962450330162,-0.4013054412792835" - "-0.3901763328819132,-0.39110921172503726,-0.39183843477145097,-0.3923748718856844,-0.39272903726671055,-0.3929110807586562]]}];" - ] - |> chartGeneratedContainsList firstSurfaceChart - ); - testCase "First surface layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" - |> chartGeneratedContains firstSurfaceChart - ); - testCase "Second surface data" ( fun () -> - """var data = [{"type":"surface","opacity":0.5,"x":[0.0,2.5],"y":[0.0,2.5],"z":[[1.0,1.0],[1.0,2.0]],"contours":{"x":{"show":true},"y":{"show":true},"z":{"show":true}}}];""" - |> chartGeneratedContains secondSurfaceChart - ); - testCase "Second surface layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" - |> chartGeneratedContains secondSurfaceChart - ); - ] - - -let meshChart = - let size = 100 - let x = linspace(-2. * Math.PI, 2. * Math.PI, size) - let y = linspace(-2. * Math.PI, 2. * Math.PI, size) - - let f x y = - (5. * x / (x**2. + y**2. + 1.) ) - - let z = - Array.init size (fun i -> - Array.init size (fun j -> - f x.[j] y.[i] - ) - ) - - let rnd = System.Random(5) - let a = Array.init 50 (fun _ -> rnd.NextDouble()) - let b = Array.init 50 (fun _ -> rnd.NextDouble()) - let c = Array.init 50 (fun _ -> rnd.NextDouble()) - - Chart.Mesh3D( - x = a, - y = b, - z = c, - FlatShading = true, - Contour = Contour.init(Show = true), - UseDefaults = false - ) - -[] -let ``Mesh charts`` = - testList "Charts3D.Mesh charts" [ - testCase "Mesh data" ( fun () -> - """var data = [{"type":"mesh3d","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382],"y":[0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116],"z":[0.1348700468125148,0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959],"contour":{"show":true},"flatshading":true}];""" - |> chartGeneratedContains meshChart - ); - testCase "Mesh layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" - |> chartGeneratedContains meshChart - ); - ] - -let coneChart = - Chart.Cone( - x = [1; 1; 1], - y = [1; 2; 3], - z = [1; 1; 1], - u = [1; 2; 3], - v = [1; 1; 2], - w = [4; 4; 1], - ColorScale = StyleParam.Colorscale.Viridis, - UseDefaults = false - ) - -[] -let ``Cone charts`` = - testList "Charts3D.Cone charts" [ - testCase "Cone data" ( fun () -> - """var data = [{"type":"cone","x":[1,1,1],"y":[1,2,3],"z":[1,1,1],"u":[1,2,3],"v":[1,1,2],"w":[4,4,1],"colorscale":"Viridis"}];""" - |> chartGeneratedContains coneChart - ); - testCase "Cone layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" - |> chartGeneratedContains coneChart - ); - ] - -let streamTubeChart = - Chart.StreamTube( - x = [0; 0; 0], - y = [0; 1; 2], - z = [0; 0; 0], - u = [0; 0; 0], - v = [1; 1; 1], - w = [0; 0; 0], - ColorScale = StyleParam.Colorscale.Viridis, - UseDefaults = false - ) - -[] -let ``StreamTube charts`` = - testList "Charts3D.StreamTube charts" [ - testCase "StreamTube data" ( fun () -> - """var data = [{"type":"streamtube","x":[0,0,0],"y":[0,1,2],"z":[0,0,0],"u":[0,0,0],"v":[1,1,1],"w":[0,0,0],"colorscale":"Viridis"}];""" - |> chartGeneratedContains streamTubeChart - ); - testCase "StreamTube layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" - |> chartGeneratedContains streamTubeChart - ); - ] - -let volumeChart = - let x,y,z = mgrid(1.,2.,4) - Chart.Volume( - x = (x |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - y = (y |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - z = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - value = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - ColorScale = StyleParam.Colorscale.Viridis, - UseDefaults = false - ) - -[] -let ``Volume charts`` = - testList "Charts3D.Volume charts" [ - testCase "Volume data" ( fun () -> - """var data = [{"type":"volume","x":[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0],"y":[1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0],"z":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"value":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"colorscale":"Viridis"}];""" - |> chartGeneratedContains volumeChart - ); - testCase "Volume layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" - |> chartGeneratedContains volumeChart - ); - ] - -let isoSurfaceChart = - let x,y,z = mgrid(1.,2.,4) - Chart.IsoSurface( - x = (x |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - y = (y |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - z = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - value = (z |> Array.concat |> Array.concat |> Array.map (fun x -> Math.Round(x,3))), - ColorScale = StyleParam.Colorscale.Viridis, - UseDefaults = false - ) - -[] -let ``IsoSurface charts`` = - testList "Charts3D.IsoSurface charts" [ - testCase "IsoSurface data" ( fun () -> - """var data = [{"type":"isosurface","x":[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0],"y":[1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0,1.333,1.333,1.333,1.333,1.667,1.667,1.667,1.667,2.0,2.0,2.0,2.0],"z":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"value":[1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0,1.0,1.333,1.667,2.0],"colorscale":"Viridis"}];""" - |> chartGeneratedContains isoSurfaceChart - ); - testCase "IsoSurface layout" ( fun () -> - """var layout = {"scene":{"camera":{"projection":{"type":"perspective"}}}};""" - |> chartGeneratedContains isoSurfaceChart - ); - ] diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/DistributionCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/DistributionCharts.fs deleted file mode 100644 index 0c457aef7..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/DistributionCharts.fs +++ /dev/null @@ -1,306 +0,0 @@ -module Tests.DistributionCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open System - -open TestUtils.HtmlCodegen - -let histoChart = - let rnd = System.Random(5) - let x = [for i=0 to 500 do yield rnd.NextDouble() ] - Chart.Histogram(X = x, UseDefaults = false) - |> Chart.withSize(500, 500) - -[] -let ``Histogram charts`` = - testList "DistributionCharts.Histogram charts" [ - testCase "Histo data" ( fun () -> - // the string is too big to be here fully. - [ - "var data = [{\"type\":\"histogram\",\"x\":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985" - """0.7608672612164483,0.8280196519699039,0.040246858280267035,0.0017312127173557937],"marker":{"pattern":{}}}];""" - ] - |> chartGeneratedContainsList histoChart - ); - testCase "Histo layout" ( fun () -> - "var layout = {\"width\":500,\"height\":500};" - |> chartGeneratedContains histoChart - ); - ] - -let box1Chart = - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] - Chart.BoxPlot(X = x, Y = y,Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All, UseDefaults = false) - -let box2Chart = - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] - Chart.BoxPlot(X = y,Y = x,Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All,Orientation=StyleParam.Orientation.Horizontal, UseDefaults = false) - -let box3Chart = - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - let y' = [2.; 1.5; 5.; 1.5; 2.; 2.5; 2.1; 2.5; 1.5; 1.;2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - [ - Chart.BoxPlot(data = y , orientation = StyleParam.Orientation.Horizontal, Name="bin1",Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All, UseDefaults = false); - Chart.BoxPlot(data = y', orientation = StyleParam.Orientation.Horizontal, Name="bin2",Jitter=0.1,BoxPoints=StyleParam.BoxPoints.All, UseDefaults = false); - ] - |> Chart.combine - -[] -let ``Box charts`` = - testList "DistributionCharts.Box charts" [ - testCase "Box1 data" ( fun () -> - """var data = [{"type":"box","x":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{},"line":{},"boxpoints":"all","jitter":0.1}];""" - |> chartGeneratedContains box1Chart - ); - testCase "Box1 layout" ( fun () -> - emptyLayout box1Chart - ); - testCase "Box2 data" ( fun () -> - """var data = [{"type":"box","x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"y":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"orientation":"h","marker":{},"line":{},"boxpoints":"all","jitter":0.1}];""" - |> chartGeneratedContains box2Chart - ); - testCase "Box2 layout" ( fun () -> - emptyLayout box2Chart - ); - testCase "Box3 data" ( fun () -> - """var data = [{"type":"box","name":"bin1","marker":{},"line":{},"boxpoints":"all","jitter":0.1,"x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]},{"type":"box","name":"bin2","marker":{},"line":{},"boxpoints":"all","jitter":0.1,"x":[2.0,1.5,5.0,1.5,2.0,2.5,2.1,2.5,1.5,1.0,2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]}];""" - |> chartGeneratedContains box3Chart - ); - testCase "Box3 layout" ( fun () -> - emptyLayout box3Chart - ); - ] - - -let violin1Chart = - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] - Chart.Violin ( - X = x, Y = y, - Points=StyleParam.JitterPoints.All, - UseDefaults = false - ) - -let violin2Chart = - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - let x = ["bin1";"bin2";"bin1";"bin2";"bin1";"bin2";"bin1";"bin1";"bin2";"bin1"] - Chart.Violin( - X = y, Y = x, - Jitter=0.1, - Points=StyleParam.JitterPoints.All, - Orientation=StyleParam.Orientation.Horizontal, - MeanLine=MeanLine.init(Visible=true), - UseDefaults = false - ) - -let violin3Chart = - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - let y' = [2.; 1.5; 5.; 1.5; 2.; 2.5; 2.1; 2.5; 1.5; 1.;2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - [ - Chart.Violin (data = y , orientation = StyleParam.Orientation.Horizontal, Name="bin1",Jitter=0.1,Points=StyleParam.BoxPoints.All, UseDefaults = false) - Chart.Violin (data = y', orientation = StyleParam.Orientation.Horizontal, Name="bin2",Jitter=0.1,Points=StyleParam.BoxPoints.All, UseDefaults = false) - ] - |> Chart.combine - -[] -let ``Violin charts`` = - testList "DistributionCharts.Violin charts" [ - testCase "Violin1 data" ( fun () -> - """var data = [{"type":"violin","x":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{},"line":{},"box":{},"points":"all"}];""" - |> chartGeneratedContains violin1Chart - ); - testCase "Violin1 layout" ( fun () -> - emptyLayout violin1Chart - ); - testCase "Violin2 data" ( fun () -> - """var data = [{"type":"violin","x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"y":["bin1","bin2","bin1","bin2","bin1","bin2","bin1","bin1","bin2","bin1"],"orientation":"h","marker":{},"line":{},"box":{},"jitter":0.1,"meanline":{"visible":true},"points":"all"}];""" - |> chartGeneratedContains violin2Chart - ); - testCase "Violin2 layout" ( fun () -> - emptyLayout violin2Chart - ); - testCase "Violin3 data" ( fun () -> - """var data = [{"type":"violin","name":"bin1","marker":{},"line":{},"box":{},"jitter":0.1,"points":"all","x":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]},{"type":"violin","name":"bin2","marker":{},"line":{},"box":{},"jitter":0.1,"points":"all","x":[2.0,1.5,5.0,1.5,2.0,2.5,2.1,2.5,1.5,1.0,2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0]}];""" - |> chartGeneratedContains violin3Chart - ); - testCase "Violin3 layout" ( fun () -> - emptyLayout violin3Chart - ); - ] - -let contourChart = - // Generate linearly spaced vector - let linspace (min,max,n) = - if n <= 2 then failwithf "n needs to be larger then 2" - let bw = float (max - min) / (float n - 1.) - [|min ..bw ..max|] - - // Create example data - let size = 100 - let x = linspace(-2. * Math.PI, 2. * Math.PI, size) - let y = linspace(-2. * Math.PI, 2. * Math.PI, size) - - let f x y = - (5. * x / (x**2. + y**2. + 1.) ) - - let z = - Array.init size (fun i -> - Array.init size (fun j -> - f x.[j] y.[i] - ) - ) - - Chart.Contour(zData = z, UseDefaults = false) - |> Chart.withSize(600.,600.) - -[] -let ``Contour charts`` = - testList "DistributionCharts.Contour charts" [ - testCase "Contour data" ( fun () -> - "var data = [{\"type\":\"contour\",\"z\":[[0.3929110807586562,0.39272903726671055,0.3923748718856843,0.3918384347714509,0.39110921172503726,0.39017633288191317,0.38902858492457726" - |> chartGeneratedContains contourChart - ); - testCase "Contour layout" ( fun () -> - "var layout = {\"width\":600,\"height\":600};" - |> chartGeneratedContains contourChart - ); - ] - -let histogramContourChart = - let normal (rnd:System.Random) mu tau = - let mutable v1 = 2.0 * rnd.NextDouble() - 1.0 - let mutable v2 = 2.0 * rnd.NextDouble() - 1.0 - let mutable r = v1 * v1 + v2 * v2 - while (r >= 1.0 || r = 0.0) do - v1 <- 2.0 * rnd.NextDouble() - 1.0 - v2 <- 2.0 * rnd.NextDouble() - 1.0 - r <- v1 * v1 + v2 * v2 - let fac = sqrt(-2.0*(log r)/r) - (tau * v1 * fac + mu) - - let rnd = System.Random(5) - let n = 2000 - let a = -1. - let b = 1.2 - let step i = a + ((b - a) / float (n - 1)) * float i - - //---------------------- generate data distributed in x and y direction ---------------------- - let x = Array.init n (fun i -> ((step i)**3.) + (0.3 * (normal (rnd) 0. 2.) )) - let y = Array.init n (fun i -> ((step i)**6.) + (0.3 * (normal (rnd) 0. 2.) )) - [ - Chart.Histogram2DContour (x = x, y = y,ContourLine=Line.init(Width=0.), UseDefaults = false) - Chart.Point(x = x,y = y,Opacity=0.3, UseDefaults = false) - ] - |> Chart.combine - - -let histogram2DChart = - let normal (rnd:System.Random) mu tau = - let mutable v1 = 2.0 * rnd.NextDouble() - 1.0 - let mutable v2 = 2.0 * rnd.NextDouble() - 1.0 - let mutable r = v1 * v1 + v2 * v2 - while (r >= 1.0 || r = 0.0) do - v1 <- 2.0 * rnd.NextDouble() - 1.0 - v2 <- 2.0 * rnd.NextDouble() - 1.0 - r <- v1 * v1 + v2 * v2 - let fac = sqrt(-2.0*(log r)/r) - (tau * v1 * fac + mu) - - let rnd = System.Random(5) - let n = 2000 - let a = -1. - let b = 1.2 - let step i = a + ((b - a) / float (n - 1)) * float i - - //---------------------- generate data distributed in x and y direction ---------------------- - let x = Array.init n (fun i -> ((step i)**3.) + (0.3 * (normal (rnd) 0. 2.) )) - let y = Array.init n (fun i -> ((step i)**6.) + (0.3 * (normal (rnd) 0. 2.) )) - Chart.Histogram2D (x = x, y = y, UseDefaults = false) - -[] -let ``Histogram 2D charts`` = - testList "DistributionCharts.Histogram charts" [ - testCase "Histo contour data" ( fun () -> - "var data = [{\"type\":\"histogram2dcontour\",\"x\":[-1.566002360265054,-1.833996340961623,-1.0330391275776571,-0.8476993487909306,-0.8471270832604864,-1.021055309868153,-0.5368298779218124,-0.9982579324563884,-0.6367576994858231,-1.433590036163408,-1.3735531103452598" - |> chartGeneratedContains histogramContourChart - ); - testCase "Histo contour layout" ( fun () -> - emptyLayout histogramContourChart - ); - testCase "Histo 2D data" ( fun () -> - "var data = [{\"type\":\"histogram2d\",\"x\":[-1.566002360265054,-1.833996340961623,-1.0330391275776571,-0.8476993487909306,-0.8471270832604864,-1.021055309868153,-0.5368298779218124,-0.9982579324563884,-0.6367576994858231,-1.433590036163408,-1.3735531103452598" - |> chartGeneratedContains histogram2DChart - ); - testCase "Histo 2D layout" ( fun () -> - emptyLayout histogram2DChart - ); - ] - - -let scatterplotMatrixChart = - let data = - [ - "A",[|1.;4.;3.4;0.7;|] - "B",[|3.;1.5;1.7;2.3;|] - "C",[|2.;4.;3.1;5.|] - "D",[|4.;2.;2.;4.;|] - ] - Chart.Splom(keyValues = data, MarkerColor=Color.fromString "blue", UseDefaults = false) - -[] -let ``Scatterplot matrix charts`` = - testList "DistributionCharts.Scatterplot matrix charts" [ - testCase "Scatterplot data" ( fun () -> - """var data = [{"type":"splom","dimensions":[{"label":"A","values":[1.0,4.0,3.4,0.7],"axis":{}},{"label":"B","values":[3.0,1.5,1.7,2.3],"axis":{}},{"label":"C","values":[2.0,4.0,3.1,5.0],"axis":{}},{"label":"D","values":[4.0,2.0,2.0,4.0],"axis":{}}],"marker":{"color":"blue"},"diagonal":{}}];""" - |> chartGeneratedContains scatterplotMatrixChart - ); - testCase "Scatterplot layout" ( fun () -> - emptyLayout scatterplotMatrixChart - ); - ] - -let pointDensityChart = - let rnd = new System.Random(5) - let x = [for i in 0 .. 100 -> rnd.NextDouble()] - let y = [for i in 0 .. 100 -> rnd.NextDouble()] - Chart.PointDensity(x = x,y = y,UseDefaults = false) - -let pointDensityChartStyled = - let rnd = new System.Random(5) - let x = [for i in 0 .. 100 -> rnd.NextDouble()] - let y = [for i in 0 .. 100 -> rnd.NextDouble()] - Chart.PointDensity( - x = x, - y = y, - PointMarkerColor = Color.fromKeyword Purple, - PointMarkerSymbol = StyleParam.MarkerSymbol.X, - PointMarkerSize = 4, - ColorScale = StyleParam.Colorscale.Viridis, - ColorBar = ColorBar.init(Title = Title.init("Density")), - ShowContourLabels = true, - UseDefaults = false - ) - -[] -let ``PointDensity charts`` = - testList "DistributionCharts.PointDensity Charts" [ - testCase "PointDensity data" ( fun () -> - """var data = [{"type":"histogram2dcontour","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"line":{"width":0.0},"contours":{"coloring":"fill","showlines":false}},{"type":"scatter","opacity":0.3,"mode":"markers","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"marker":{}}];""" - |> chartGeneratedContains pointDensityChart - ); - testCase "PointDensity layout" ( fun () -> - emptyLayout pointDensityChart - ); - testCase "PointDensity styled data" ( fun () -> - """var data = [{"type":"histogram2dcontour","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"line":{"width":0.0},"colorbar":{"title":{"text":"Density"}},"colorscale":"Viridis","contours":{"coloring":"fill","showlabels":true,"showlines":false}},{"type":"scatter","opacity":0.3,"mode":"markers","x":[0.33836984091362443,0.2844184475412678,0.2629626417825756,0.6253758443637638,0.46346185284827923,0.9283738280312968,0.1463105539541275,0.9505998873853124,0.5961332552116985,0.11745994590104555,0.975558924477342,0.37088692624628866,0.0699143670824889,0.07078822472635109,0.48201058175508427,0.15297147219673332,0.9641655045394625,0.09534371648698287,0.8125330809562156,0.2506162050415837,0.48126059979259067,0.07473527084790882,0.8369272271343168,0.7793545950107996,0.18997055114711195,0.7421991949631829,0.2328434778530353,0.7856600809775572,0.9278804142623583,0.10790790343094053,0.03301328235911824,0.770361295794305,0.30779169793603556,0.11389689665003536,0.388590590743623,0.9796536713743832,0.17214082375734152,0.7884985966554371,0.1994013894346549,0.7964705586416976,0.3436089406458703,0.10509170037931376,0.9796912223006092,0.8392714871276503,0.5432778380547081,0.1979652751227679,0.038267011306372944,0.5355382620056803,0.6352935864754456,0.8821615948724382,0.9909701128448221,0.9722291035448336,0.2536179266188377,0.5066026125599642,0.19606175189654423,0.2636345700657156,0.447491220406951,0.48360804677177593,0.4354052932166519,0.7212626578850964,0.6955303501782615,0.3606504729765702,0.022719473122954123,0.48822535178075793,0.08444666354192731,0.20519762868303695,0.06309522831025312,0.9560174704324536,0.682197633982728,0.5023569103807011,0.9808306484393918,0.17566690788402545,0.8959423270523279,0.016062522314518,0.9070072643957134,0.37616889941327686,0.0950440485472996,0.9976557400066665,0.2360767569560915,0.9920052760243441,0.70393218365681,0.6973052158473549,0.15036649450211156,0.04571881938992013,0.11693779058611849,0.060784178814284585,0.5167433691754674,0.8011890853760714,0.9178351447534912,0.1249560206779074,0.5321624509674322,0.6885327769855656,0.35309330343878514,0.47813873154955855,0.10094020846343608,0.9829584676693001,0.08237222725635963,0.4914658705198513,0.754824823585723,0.33808020937167116,0.1348700468125148],"y":[0.0822495408739194,0.8533406280229523,0.13293667609474466,0.9013309464330463,0.8153032049607966,0.07628677649250569,0.2375554043043197,0.5995953481642508,0.8198928524832674,0.16859052151841603,0.44983548040028454,0.24753128981568445,0.44340001719230787,0.017330474228286406,0.9982251343309065,0.21028397847445868,0.977000653733034,0.37128756119463946,0.023662484727642725,0.6884542595075696,0.2619061429341818,0.03818232567896243,0.5572416133048207,0.9701944594132688,0.29229787145382624,0.8225736044452403,0.4178035955027694,0.9151223138510819,0.9240487967264135,0.29379667215691724,0.6035781780274483,0.24283091642094354,0.8979965475844204,0.8571352292118293,0.6216826427828905,0.8439645878244026,0.0174298184073669,0.1443878729568738,0.30163458562532186,0.9844974023217788,0.2791879648711476,0.20159373721182056,0.09794229227022375,0.9563654991594914,0.0823269705671477,0.44100148716988113,0.9096932862464773,0.4608082573212722,0.10271507413252959,0.7726744891948414,0.10537157352332564,0.12017830932521183,0.7311623388580802,0.6496259498641016,0.872963903878333,0.04406721519495697,0.29609901471813166,0.16274221668147584,0.6090330749792201,0.9927296005155564,0.6584831809897363,0.3224330904532378,0.6755465514378374,0.5260961803263501,0.5650123434909677,0.20700456397934097,0.34953474223126413,0.5862647879804787,0.3956478314453959,0.15426054650650387,0.19285416006709177,0.8326127807761602,0.06965297556931757,0.03916508240586383,0.409266294636422,0.06031240572236125,0.9402400334087387,0.6008761141453293,0.8878674888461211,0.8512963842839452,0.912880318198763,0.9569953381814972,0.8124072397185523,0.15137430753157208,0.1884250986335916,0.4833998687022365,0.5116685775628633,0.24837059772031875,0.9841713253334963,0.5776154275879336,0.547865573106271,0.8546876017258911,0.5353547979776537,0.30890498510976555,0.3260142213320426,0.9567548744179099,0.5260471857739832,0.5718461119438736,0.3913531556685237,0.8753224065878067,0.09146674493861699],"marker":{"color":"rgba(128, 0, 128, 1.0)","size":4,"symbol":"4"}}];""" - |> chartGeneratedContains pointDensityChartStyled - ); - testCase "PointDensity styled layout" ( fun () -> - emptyLayout pointDensityChartStyled - ); - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/FinanceCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/FinanceCharts.fs deleted file mode 100644 index b12d7ed11..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/FinanceCharts.fs +++ /dev/null @@ -1,208 +0,0 @@ -module Tests.FinanceCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open System - -open TestUtils.HtmlCodegen - -let candles = - [|("2020-01-17T13:40:00", 0.68888, 0.68888, 0.68879, 0.6888); - ("2020-01-17T13:41:00", 0.68883, 0.68884, 0.68875, 0.68877); - ("2020-01-17T13:42:00", 0.68878, 0.68889, 0.68878, 0.68886); - ("2020-01-17T13:43:00", 0.68886, 0.68886, 0.68876, 0.68879); - ("2020-01-17T13:44:00", 0.68879, 0.68879, 0.68873, 0.68874); - ("2020-01-17T13:45:00", 0.68875, 0.68877, 0.68867, 0.68868); - ("2020-01-17T13:46:00", 0.68869, 0.68887, 0.68869, 0.68883); - ("2020-01-17T13:47:00", 0.68883, 0.68899, 0.68883, 0.68899); - ("2020-01-17T13:48:00", 0.68898, 0.689, 0.68885, 0.68889); - ("2020-01-17T13:49:00", 0.68889, 0.68893, 0.68881, 0.68893); - ("2020-01-17T13:50:00", 0.68891, 0.68896, 0.68886, 0.68891); - |] - |> Array.map (fun (d,o,h,l,c)->System.DateTime.Parse d, StockData.Create(o,h,l,c)) - -let ohlc1Chart = Chart.OHLC(stockTimeSeries = candles, UseDefaults = false) - -let ohlc2Chart = - Chart.OHLC(stockTimeSeries = candles, ShowXAxisRangeSlider = false, UseDefaults = false) - - -[] -let ``OHLC charts`` = - testList "FinanceCharts.OHLC charts" [ - testCase "OHLC 1 data" ( fun () -> - """var data = [{"type":"ohlc","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" - |> chartGeneratedContains ohlc1Chart - ); - testCase "OHLC 1 layout" ( fun () -> - """var layout = {"xaxis":{"rangeslider":{"yaxis":{}}}};""" - |> chartGeneratedContains ohlc1Chart - ); - testCase "OHLC 2 data" ( fun () -> - """var data = [{"type":"ohlc","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" - |> chartGeneratedContains ohlc2Chart - ); - testCase "OHLC 2 layout" ( fun () -> - """var layout = {"xaxis":{"rangeslider":{"visible":false,"yaxis":{}}}};""" - |> chartGeneratedContains ohlc2Chart - ); - ] - -let candles1Chart = Chart.Candlestick(stockTimeSeries = candles, UseDefaults = false) - -let candles2Chart = - Chart.Candlestick(stockTimeSeries = candles, ShowXAxisRangeSlider = false, UseDefaults = false) - - -[] -let ``Candlestick charts`` = - testList "FinanceCharts.Candlestick charts" [ - testCase "Candlestick 1 data" ( fun () -> - """var data = [{"type":"candlestick","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" - |> chartGeneratedContains candles1Chart - ); - testCase "Candlestick 1 layout" ( fun () -> - """var layout = {"xaxis":{"rangeslider":{"yaxis":{}}}};""" - |> chartGeneratedContains candles1Chart - ); - testCase "Candlestick 2 data" ( fun () -> - """var data = [{"type":"candlestick","x":["2020-01-17T13:40:00","2020-01-17T13:41:00","2020-01-17T13:42:00","2020-01-17T13:43:00","2020-01-17T13:44:00","2020-01-17T13:45:00","2020-01-17T13:46:00","2020-01-17T13:47:00","2020-01-17T13:48:00","2020-01-17T13:49:00","2020-01-17T13:50:00"],"close":[0.6888,0.68877,0.68886,0.68879,0.68874,0.68868,0.68883,0.68899,0.68889,0.68893,0.68891],"open":[0.68888,0.68883,0.68878,0.68886,0.68879,0.68875,0.68869,0.68883,0.68898,0.68889,0.68891],"high":[0.68888,0.68884,0.68889,0.68886,0.68879,0.68877,0.68887,0.68899,0.689,0.68893,0.68896],"low":[0.68879,0.68875,0.68878,0.68876,0.68873,0.68867,0.68869,0.68883,0.68885,0.68881,0.68886],"increasing":{"line":{}},"decreasing":{"line":{}}}];""" - |> chartGeneratedContains candles2Chart - ); - testCase "Candlestick 2 layout" ( fun () -> - """var layout = {"xaxis":{"rangeslider":{"visible":false,"yaxis":{}}}};""" - |> chartGeneratedContains candles2Chart - ); - ] - - -let funnelChart = - let y = [|"Sales person A"; "Sales person B"; "Sales person C"; "Sales person D"; "Sales person E"|] - let x = [|1200.; 909.4; 600.6; 300.; 80.|] - - // Customize the connector lines used to connect the funnel bars - let connectorLine = Line.init (Color=Color.fromString "royalblue", Dash=StyleParam.DrawingStyle.Dot, Width=3.) - let connector = FunnelConnector.init(Line=connectorLine) - - // Customize the outline of the funnel bars - let line = Line.init(Width=2.,Color=Color.fromString "3E4E88") - - Chart.Funnel (x = x, y = y,MarkerColor=Color.fromString "59D4E8", MarkerOutline=line, Connector=connector, UseDefaults = false) - |> Chart.withMarginSize(Left=100) - -[] -let ``Funnel charts`` = - testList "FinanceCharts.Funnel charts" [ - testCase "Funnel data" ( fun () -> - """var data = [{"type":"funnel","x":[1200.0,909.4,600.6,300.0,80.0],"y":["Sales person A","Sales person B","Sales person C","Sales person D","Sales person E"],"marker":{"color":"59D4E8","line":{"color":"3E4E88","width":2.0}},"connector":{"line":{"color":"royalblue","width":3.0,"dash":"dot"}}}];""" - |> chartGeneratedContains funnelChart - ); - testCase "Funnel layout" ( fun () -> - "var layout = {\"margin\":{\"l\":100}};" - |> chartGeneratedContains funnelChart - ); - ] - - - -let funnelArea = - let values = [|5; 4; 3; 2; 1|] - let text = [|"The 1st"; "The 2nd"; "The 3rd"; "The 4th"; "The 5th"|] - let line = Line.init (Color=Color.fromString "purple", Width=3.) - Chart.FunnelArea(values=values, MultiText=text, SectionOutline=line, UseDefaults = false) - - -let funnelAreaStyled = - let values = [|5; 4; 3|] - let labels = [|"The 1st"; "The 2nd"; "The 3rd"|] - - Chart.FunnelArea( - values = values, - Labels = labels, - MultiText = labels, - SectionColors = [ - Color.fromKeyword Aqua - Color.fromKeyword Salmon - Color.fromKeyword Tan - ], - SectionOutlineColor = Color.fromKeyword Black, - SectionOutlineWidth = 2., - AspectRatio = 0.75, - BaseRatio = 0.1, - UseDefaults = false - ) - - -[] -let ``Funnel area charts`` = - testList "FinanceCharts.Funnel area charts" [ - testCase "Funnel area data" ( fun () -> - """var data = [{"type":"funnelarea","values":[5,4,3,2,1],"text":["The 1st","The 2nd","The 3rd","The 4th","The 5th"],"marker":{"line":{"color":"purple","width":3.0}}}];""" - |> chartGeneratedContains funnelArea - ); - testCase "Funnel area layout" ( fun () -> - emptyLayout funnelArea - ); - testCase "Funnel area styled data" ( fun () -> - """var data = [{"type":"funnelarea","values":[5,4,3],"labels":["The 1st","The 2nd","The 3rd"],"text":["The 1st","The 2nd","The 3rd"],"marker":{"colors":["rgba(0, 255, 255, 1.0)","rgba(250, 128, 114, 1.0)","rgba(210, 180, 140, 1.0)"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":2.0}},"aspectratio":0.75,"baseratio":0.1}];""" - |> chartGeneratedContains funnelAreaStyled - ); - testCase "Funnel area styled layout" ( fun () -> - emptyLayout funnelAreaStyled - ); - ] - -let indicators = - [ - ChartDomain.Chart.Indicator( - value = 200., - mode = StyleParam.IndicatorMode.NumberDeltaGauge, - Delta = IndicatorDelta.init(Reference=160), - Range = StyleParam.Range.MinMax(0., 250.), - Domain = Domain.init(Row = 0, Column = 0), - UseDefaults = false - ) - Chart.Indicator( - value = 120, - mode = StyleParam.IndicatorMode.NumberDeltaGauge, - DeltaReference = 90, - Range = StyleParam.Range.MinMax(-200., 200.), - GaugeShape = StyleParam.IndicatorGaugeShape.Bullet, - ShowGaugeAxis = false, - Domain = Domain.init(Row = 0, Column = 1), - UseDefaults = false - ) - Chart.Indicator( - value = "300", - mode = StyleParam.IndicatorMode.NumberDelta, - DeltaReference = 90., - Domain = Domain.init(Row = 1, Column = 0), - UseDefaults = false - ) - Chart.Indicator( - value = 40., - mode = StyleParam.IndicatorMode.Delta, - DeltaReference = 90., - Domain = Domain.init(Row = 1, Column = 1), - UseDefaults = false - ) - ] - |> Chart.combine - |> Chart.withLayoutGridStyle(Rows = 2, Columns = 2) - - -[] -let ``Indicator charts`` = - testList "FinanceCharts.Indicator charts" [ - testCase "Indicator data" ( fun () -> - """var data = [{"type":"indicator","mode":"number+delta+gauge","value":200.0,"domain":{"row":0,"column":0},"delta":{"reference":160},"gauge":{"axis":{"range":[0.0,250.0]}}},{"type":"indicator","mode":"number+delta+gauge","value":120,"domain":{"row":0,"column":1},"delta":{"reference":90},"gauge":{"axis":{"visible":false,"range":[-200.0,200.0]},"shape":"bullet"}},{"type":"indicator","mode":"number+delta","value":"300","domain":{"row":1,"column":0},"delta":{"reference":90.0},"gauge":{"axis":{}}},{"type":"indicator","mode":"delta","value":40.0,"domain":{"row":1,"column":1},"delta":{"reference":90.0},"gauge":{"axis":{}}}];""" - |> chartGeneratedContains indicators - ); - testCase "Indicator layout" ( fun () -> - """var layout = {"grid":{"rows":2,"columns":2}};""" - |> chartGeneratedContains indicators - ); - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/GeoMapCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/GeoMapCharts.fs deleted file mode 100644 index d65e00dfd..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/GeoMapCharts.fs +++ /dev/null @@ -1,269 +0,0 @@ -module Tests.GeoMapCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open Plotly.NET.LayoutObjects -open System - -open TestUtils.HtmlCodegen - -let basemapChart = - Chart.PointGeo(locations = [], UseDefaults = false) // deliberately empty chart to show the base map only - |> Chart.withMarginSize(0, 0, 0, 0) - -let moreFeaturesBasemapChart = - let myGeo = - Geo.init( - Resolution=StyleParam.GeoResolution.R50, - ShowCoastLines=true, - CoastLineColor=Color.fromString "RebeccaPurple", - ShowLand=true, - LandColor=Color.fromString "LightGreen", - ShowOcean=true, - OceanColor=Color.fromString "LightBlue", - ShowLakes=true, - LakeColor=Color.fromString "Blue", - ShowRivers=true, - RiverColor=Color.fromString "Blue" - ) - Chart.PointGeo(locations = [], UseDefaults = false) - |> Chart.withGeo myGeo - |> Chart.withMarginSize(0, 0, 0, 0) - -let cultureMapChart = - let countryGeo = - Geo.init( - Visible=false, - Resolution=StyleParam.GeoResolution.R50, - ShowCountries=true, - CountryColor=Color.fromString "RebeccaPurple" - ) - Chart.PointGeo(locations = [], UseDefaults = false) - |> Chart.withGeo countryGeo - |> Chart.withMarginSize(0, 0, 0, 0) - -[] -let ``Geo charts`` = - testList "GeoMapCharts.Geo charts" [ - testCase "Basemap data" ( fun () -> - """var data = [{"type":"scattergeo","mode":"markers","locations":[],"marker":{},"line":{}}];""" - |> chartGeneratedContains basemapChart - ); - testCase "Basemap layout" ( fun () -> - "var layout = {\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" - |> chartGeneratedContains basemapChart - ); - testCase "More features map data" ( fun () -> - """var data = [{"type":"scattergeo","mode":"markers","locations":[],"marker":{},"line":{}}];""" - |> chartGeneratedContains moreFeaturesBasemapChart - ); - testCase "More features map layout" ( fun () -> - "var layout = {\"geo\":{\"resolution\":\"50\",\"showcoastline\":true,\"coastlinecolor\":\"RebeccaPurple\",\"showland\":true,\"landcolor\":\"LightGreen\",\"showocean\":true,\"oceancolor\":\"LightBlue\",\"showlakes\":true,\"lakecolor\":\"Blue\",\"showrivers\":true,\"rivercolor\":\"Blue\"},\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" - |> chartGeneratedContains moreFeaturesBasemapChart - ); - testCase "Cultural map data" ( fun () -> - """var data = [{"type":"scattergeo","mode":"markers","locations":[],"marker":{},"line":{}}];""" - |> chartGeneratedContains cultureMapChart - ); - testCase "Cultural map layout" ( fun () -> - "var layout = {\"geo\":{\"resolution\":\"50\",\"visible\":false,\"showcountries\":true,\"countrycolor\":\"RebeccaPurple\"},\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" - |> chartGeneratedContains cultureMapChart - ); - ] - - -let pointGeoChart = - let cityNames = [ - "Montreal"; "Toronto"; "Vancouver"; "Calgary"; "Edmonton"; - "Ottawa"; "Halifax"; "Victoria"; "Winnepeg"; "Regina" - ] - - let lon = [ - -73.57; -79.24; -123.06; -114.1; -113.28; - -75.43; -63.57; -123.21; -97.13; -104.6 - ] - let lat = [ - 45.5; 43.4; 49.13; 51.1; 53.34; 45.24; - 44.64; 48.25; 49.89; 50.45 - ] - Chart.PointGeo( - longitudes = lon, - latitudes = lat, - MultiText=cityNames, - TextPosition=StyleParam.TextPosition.TopCenter, - UseDefaults = false - ) - |> Chart.withGeoStyle( - Scope=StyleParam.GeoScope.NorthAmerica, - Projection=GeoProjection.init(StyleParam.GeoProjectionType.AzimuthalEqualArea), - CountryColor = Color.fromString "lightgrey" - ) - |> Chart.withMarginSize(0, 0, 0, 0) - -open Deedle -open FSharp.Data -open System.IO -open System.Text - -let flightsMapChart = - // this is not the original string, but its few first entries - let data = - "start_lat,start_lon,end_lat,end_lon,airline,airport1,airport2,cnt -32.89595056,-97.0372,35.04022222,-106.6091944,AA,DFW,ABQ,444 -41.979595,-87.90446417,30.19453278,-97.66987194,AA,ORD,AUS,166 -32.89595056,-97.0372,41.93887417,-72.68322833,AA,DFW,BDL,162 -18.43941667,-66.00183333,41.93887417,-72.68322833,AA,SJU,BDL,56 -32.89595056,-97.0372,33.56294306,-86.75354972,AA,DFW,BHM,168 -25.79325,-80.29055556,36.12447667,-86.67818222,AA,MIA,BNA,56 -32.89595056,-97.0372,42.3643475,-71.00517917,AA,DFW,BOS,422 -25.79325,-80.29055556,42.3643475,-71.00517917,AA,MIA,BOS,392 -41.979595,-87.90446417,42.3643475,-71.00517917,AA,ORD,BOS,430 -18.43941667,-66.00183333,42.3643475,-71.00517917,AA,SJU,BOS,56 -18.33730556,-64.97336111,42.3643475,-71.00517917,AA,STT,BOS,44 -25.79325,-80.29055556,39.17540167,-76.66819833,AA,MIA,BWI,112" - |> fun csv -> Frame.ReadCsvString(csv,true,separators=",") - - let opacityVals : float [] = data.["cnt"] |> Series.values |> fun s -> s |> Seq.map (fun v -> v/(Seq.max s)) |> Array.ofSeq - let startCoords = Series.zipInner data.["start_lon"] data.["start_lat"] - let endCoords = Series.zipInner data.["end_lon"] data.["end_lat"] - let coords = Series.zipInner startCoords endCoords |> Series.values - - coords - |> Seq.mapi (fun i (startCoords,endCoords) -> - Chart.LineGeo( - lonlat = [startCoords; endCoords], - Opacity = opacityVals.[i], - MarkerColor = Color.fromString "red", - UseDefaults = false - ) - ) - |> Chart.combine - |> Chart.withLegend(false) - |> Chart.withGeoStyle( - Scope=StyleParam.GeoScope.NorthAmerica, - Projection=GeoProjection.init(StyleParam.GeoProjectionType.AzimuthalEqualArea), - ShowLand=true, - LandColor = Color.fromString "lightgrey" - ) - |> Chart.withMarginSize(0,0,50,0) - |> Chart.withTitle "Feb. 2011 American Airline flights" - - -[] -let ``Scatter and line plots on Geo maps charts`` = - testList "GeoMapCharts.Scatter and line plots on Geo maps charts" [ - testCase "Point geo data" ( fun () -> - """var data = [{"type":"scattergeo","mode":"markers+text","lat":[45.5,43.4,49.13,51.1,53.34,45.24,44.64,48.25,49.89,50.45],"lon":[-73.57,-79.24,-123.06,-114.1,-113.28,-75.43,-63.57,-123.21,-97.13,-104.6],"text":["Montreal","Toronto","Vancouver","Calgary","Edmonton","Ottawa","Halifax","Victoria","Winnepeg","Regina"],"textposition":"top center","marker":{},"line":{}}];""" - |> chartGeneratedContains pointGeoChart - ); - testCase "Point geo layout" ( fun () -> - "var layout = {\"geo\":{\"scope\":\"north america\",\"projection\":{\"type\":\"azimuthal equal area\"},\"countrycolor\":\"lightgrey\"},\"margin\":{\"l\":0,\"r\":0,\"t\":0,\"b\":0}};" - |> chartGeneratedContains pointGeoChart - ); - testCase "Flight map data" ( fun () -> - """var data = [{"type":"scattergeo","opacity":1.0,"mode":"lines","lat":[32.89595056,35.04022222],"lon":[-97.0372,-106.6091944],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.3738738738738739,"mode":"lines","lat":[41.979595,30.19453278],"lon":[-87.90446417,-97.66987194],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.36486486486486486,"mode":"lines","lat":[32.89595056,41.93887417],"lon":[-97.0372,-72.68322833],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.12612612612612611,"mode":"lines","lat":[18.43941667,41.93887417],"lon":[-66.00183333,-72.68322833],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.3783783783783784,"mode":"lines","lat":[32.89595056,33.56294306],"lon":[-97.0372,-86.75354972],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.12612612612612611,"mode":"lines","lat":[25.79325,36.12447667],"lon":[-80.29055556,-86.67818222],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.9504504504504504,"mode":"lines","lat":[32.89595056,42.3643475],"lon":[-97.0372,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.8828828828828829,"mode":"lines","lat":[25.79325,42.3643475],"lon":[-80.29055556,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.9684684684684685,"mode":"lines","lat":[41.979595,42.3643475],"lon":[-87.90446417,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.12612612612612611,"mode":"lines","lat":[18.43941667,42.3643475],"lon":[-66.00183333,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.0990990990990991,"mode":"lines","lat":[18.33730556,42.3643475],"lon":[-64.97336111,-71.00517917],"marker":{"color":"red"},"line":{}},{"type":"scattergeo","opacity":0.25225225225225223,"mode":"lines","lat":[25.79325,39.17540167],"lon":[-80.29055556,-76.66819833],"marker":{"color":"red"},"line":{}}];""" - |> chartGeneratedContains flightsMapChart - ); - testCase "Flight map layout" ( fun () -> - "var layout = {\"showlegend\":false,\"geo\":{\"scope\":\"north america\",\"projection\":{\"type\":\"azimuthal equal area\"},\"showland\":true,\"landcolor\":\"lightgrey\"},\"margin\":{\"l\":0,\"r\":0,\"t\":50,\"b\":0},\"title\":{\"text\":\"Feb. 2011 American Airline flights\"}};" - |> chartGeneratedContains flightsMapChart - ); - ] - - -let locations,z = - [("Belarus",17.5); ("Moldova",16.8);("Lithuania",15.4);("Russia",15.1); - ("Romania",14.4);("Ukraine",13.9);("Andorra",13.8);("Hungary",13.3); - ("Czech Republic",13.);("Slovakia",13.);("Portugal",12.9);("Serbia",12.6); - ("Grenada",12.5);("Poland",12.5);("Latvia",12.3);("Finland",12.3); - ("South Korea",12.3);("France",12.2);("Australia",12.2);("Croatia",12.2); - ("Ireland",11.9);("Luxembourg",11.9);("Germany",11.8);("Slovenia",11.6); - ("United Kingdom",11.6);("Denmark",11.4);("Bulgaria",11.4);("Spain",11.2); - ("Belgium",11.);("South Africa",11.);("New Zealand",10.9);("Gabon",10.9); - ("Namibia",10.8);("Switzerland",10.7);("Saint Lucia",10.4);("Austria",10.3); - ("Estonia",10.3);("Greece",10.3);("Kazakhstan",10.3);("Canada",10.2); - ("Nigeria",10.1);("Netherlands",9.9);("Uganda",9.8);("Rwanda",9.8); - ("Chile",9.6);("Argentina",9.3);("Burundi",9.3);("United States",9.2); - ("Cyprus",9.2);("Sweden",9.2);("Venezuela",8.9);("Paraguay",8.8);("Brazil",8.7); - ("Sierra Leone",8.7);("Montenegro",8.7);("Belize",8.5);("Cameroon",8.4); - ("Botswana",8.4);("Saint Kitts and Nevis",8.2);("Guyana",8.1);("Peru",8.1); - ("Panama",8.);("Niue",8.);("Palau",7.9);("Norway",7.7);("Tanzania",7.7); - ("Georgia",7.7);("Uruguay",7.6);("Angola",7.5);("Laos",7.3);("Japan",7.2); - ("Mexico",7.2);("Ecuador",7.2);("Dominica",7.1);("Iceland",7.1); - ("Thailand",7.1);("Bosnia and Herzegovina",7.1);("Sao Tome and Principe",7.1); - ("Malta",7.);("Albania",7.);("Bahamas",6.9);("Dominican Republic",6.9); - ("Mongolia",6.9);("Cape Verde",6.9);("Barbados",6.8);("Burkina Faso",6.8); - ("Italy",6.7);("Trinidad and Tobago",6.7);("China",6.7);("Macedonia",6.7); - ("Saint Vincent and the Grenadines",6.6);("Equatorial Guinea",6.6); - ("Suriname",6.6);("Vietnam",6.6);("Lesotho",6.5);("Haiti",6.4); - ("Cook Islands",6.4);("Colombia",6.2);("Ivory Coast",6.);("Bolivia",5.9); - ("Swaziland",5.7);("Zimbabwe",5.7);("Seychelles",5.6);("Cambodia",5.5); - ("Puerto Rico",5.4);("Netherlands Antilles",5.4);("Philippines",5.4); - ("Costa Rica",5.4);("Armenia",5.3);("Cuba",5.2);("Nicaragua",5.); - ("Jamaica",4.9);("Ghana",4.8);("Liberia",4.7);("Uzbekistan",4.6); - ("Chad",4.4);("United Arab Emirates",4.3);("Kyrgyzstan",4.3);("India",4.3); - ("Turkmenistan",4.3);("Kenya",4.3);("Ethiopia",4.2);("Honduras",4.); - ("Guinea-Bissau",4.);("Zambia",4.);("Republic of the Congo",3.9);("Guatemala",3.8); - ("Central African Republic",3.8);("North Korea",3.7);("Sri Lanka",3.7); - ("Mauritius",3.6);("Samoa",3.6);("Democratic Republic of the Congo",3.6); - ("Nauru",3.5);("Gambia",3.4);("Federated States of Micronesia",3.3); - ("El Salvador",3.2);("Fiji",3.);("Papua New Guinea",3.);("Kiribati",3.); - ("Tajikistan",2.8);("Israel",2.8);("Sudan",2.7);("Malawi",2.5);("Lebanon",2.4); - ("Azerbaijan",2.3);("Mozambique",2.3);("Togo",2.3);("Nepal",2.2);("Brunei",2.1); - ("Benin",2.1);("Singapore",2.);("Turkey",2.);("Madagascar",1.8);("Solomon Islands",1.7); - ("Tonga",1.6);("Tunisia",1.5);("Tuvalu",1.5);("Qatar",1.5);("Vanuatu",1.4); - ("Djibouti",1.3);("Malaysia",1.3);("Syria",1.2);("Maldives",1.2);("Mali",1.1); - ("Eritrea",1.1);("Algeria",1.);("Iran",1.);("Oman",0.9);("Brunei",0.9); - ("Morocco",0.9);("Jordan",0.7);("Bhutan",0.7);("Guinea",0.7);("Burma",0.7); - ("Afghanistan",0.7);("Senegal",0.6);("Indonesia",0.6);("Timor-Leste",0.6); - ("Iraq",0.5);("Somalia",0.5);("Egypt",0.4);("Niger",0.3);("Yemen",0.3); - ("Comoros",0.2);("Saudi Arabia",0.2);("Bangladesh",0.2);("Kuwait",0.1); - ("Libya",0.1);("Mauritania",0.1);("Pakistan",0.1);] - |> List.unzip - - -let choroplethMap1Chart = - Chart.ChoroplethMap( - locations = locations,z = z, - LocationMode=StyleParam.LocationFormat.CountryNames, - UseDefaults = false - ) - -let choroplethMap2Chart = - Chart.ChoroplethMap( - locations = locations,z = z, - LocationMode=StyleParam.LocationFormat.CountryNames, - UseDefaults = false - ) - |> Chart.withGeoStyle( - Projection=GeoProjection.init(projectionType=StyleParam.GeoProjectionType.Mollweide), - ShowLakes=true, - ShowOcean=true, - OceanColor= Color.fromString "lightblue", - ShowRivers=true) - |> Chart.withColorBarStyle ( - TitleText="Alcohol consumption[l/y]",Len=0.5 - ) - -[] -let ``Choropleth maps charts`` = - testList "GeoMapCharts.Choropleth maps charts" [ - testCase "Choropleth map 1 data" ( fun () -> - """var data = [{"type":"choropleth","z":[17.5,16.8,15.4,15.1,14.4,13.9,13.8,13.3,13.0,13.0,12.9,12.6,12.5,12.5,12.3,12.3,12.3,12.2,12.2,12.2,11.9,11.9,11.8,11.6,11.6,11.4,11.4,11.2,11.0,11.0,10.9,10.9,10.8,10.7,10.4,10.3,10.3,10.3,10.3,10.2,10.1,9.9,9.8,9.8,9.6,9.3,9.3,9.2,9.2,9.2,8.9,8.8,8.7,8.7,8.7,8.5,8.4,8.4,8.2,8.1,8.1,8.0,8.0,7.9,7.7,7.7,7.7,7.6,7.5,7.3,7.2,7.2,7.2,7.1,7.1,7.1,7.1,7.1,7.0,7.0,6.9,6.9,6.9,6.9,6.8,6.8,6.7,6.7,6.7,6.7,6.6,6.6,6.6,6.6,6.5,6.4,6.4,6.2,6.0,5.9,5.7,5.7,5.6,5.5,5.4,5.4,5.4,5.4,5.3,5.2,5.0,4.9,4.8,4.7,4.6,4.4,4.3,4.3,4.3,4.3,4.3,4.2,4.0,4.0,4.0,3.9,3.8,3.8,3.7,3.7,3.6,3.6,3.6,3.5,3.4,3.3,3.2,3.0,3.0,3.0,2.8,2.8,2.7,2.5,2.4,2.3,2.3,2.3,2.2,2.1,2.1,2.0,2.0,1.8,1.7,1.6,1.5,1.5,1.5,1.4,1.3,1.3,1.2,1.2,1.1,1.1,1.0,1.0,0.9,0.9,0.9,0.7,0.7,0.7,0.7,0.7,0.6,0.6,0.6,0.5,0.5,0.4,0.3,0.3,0.2,0.2,0.2,0.1,0.1,0.1,0.1],"locations":["Belarus","Moldova","Lithuania","Russia","Romania","Ukraine","Andorra","Hungary","Czech Republic","Slovakia","Portugal","Serbia","Grenada","Poland","Latvia","Finland","South Korea","France","Australia","Croatia","Ireland","Luxembourg","Germany","Slovenia","United Kingdom","Denmark","Bulgaria","Spain","Belgium","South Africa","New Zealand","Gabon","Namibia","Switzerland","Saint Lucia","Austria","Estonia","Greece","Kazakhstan","Canada","Nigeria","Netherlands","Uganda","Rwanda","Chile","Argentina","Burundi","United States","Cyprus","Sweden","Venezuela","Paraguay","Brazil","Sierra Leone","Montenegro","Belize","Cameroon","Botswana","Saint Kitts and Nevis","Guyana","Peru","Panama","Niue","Palau","Norway","Tanzania","Georgia","Uruguay","Angola","Laos","Japan","Mexico","Ecuador","Dominica","Iceland","Thailand","Bosnia and Herzegovina","Sao Tome and Principe","Malta","Albania","Bahamas","Dominican Republic","Mongolia","Cape Verde","Barbados","Burkina Faso","Italy","Trinidad and Tobago","China","Macedonia","Saint Vincent and the Grenadines","Equatorial Guinea","Suriname","Vietnam","Lesotho","Haiti","Cook Islands","Colombia","Ivory Coast","Bolivia","Swaziland","Zimbabwe","Seychelles","Cambodia","Puerto Rico","Netherlands Antilles","Philippines","Costa Rica","Armenia","Cuba","Nicaragua","Jamaica","Ghana","Liberia","Uzbekistan","Chad","United Arab Emirates","Kyrgyzstan","India","Turkmenistan","Kenya","Ethiopia","Honduras","Guinea-Bissau","Zambia","Republic of the Congo","Guatemala","Central African Republic","North Korea","Sri Lanka","Mauritius","Samoa","Democratic Republic of the Congo","Nauru","Gambia","Federated States of Micronesia","El Salvador","Fiji","Papua New Guinea","Kiribati","Tajikistan","Israel","Sudan","Malawi","Lebanon","Azerbaijan","Mozambique","Togo","Nepal","Brunei","Benin","Singapore","Turkey","Madagascar","Solomon Islands","Tonga","Tunisia","Tuvalu","Qatar","Vanuatu","Djibouti","Malaysia","Syria","Maldives","Mali","Eritrea","Algeria","Iran","Oman","Brunei","Morocco","Jordan","Bhutan","Guinea","Burma","Afghanistan","Senegal","Indonesia","Timor-Leste","Iraq","Somalia","Egypt","Niger","Yemen","Comoros","Saudi Arabia","Bangladesh","Kuwait","Libya","Mauritania","Pakistan"],"locationmode":"country names"}];""" - |> chartGeneratedContains choroplethMap1Chart - ); - testCase "Choropleth map 1 layout" ( fun () -> - emptyLayout choroplethMap1Chart - ); - testCase "Choropleth map 2 data" ( fun () -> - """var data = [{"type":"choropleth","z":[17.5,16.8,15.4,15.1,14.4,13.9,13.8,13.3,13.0,13.0,12.9,12.6,12.5,12.5,12.3,12.3,12.3,12.2,12.2,12.2,11.9,11.9,11.8,11.6,11.6,11.4,11.4,11.2,11.0,11.0,10.9,10.9,10.8,10.7,10.4,10.3,10.3,10.3,10.3,10.2,10.1,9.9,9.8,9.8,9.6,9.3,9.3,9.2,9.2,9.2,8.9,8.8,8.7,8.7,8.7,8.5,8.4,8.4,8.2,8.1,8.1,8.0,8.0,7.9,7.7,7.7,7.7,7.6,7.5,7.3,7.2,7.2,7.2,7.1,7.1,7.1,7.1,7.1,7.0,7.0,6.9,6.9,6.9,6.9,6.8,6.8,6.7,6.7,6.7,6.7,6.6,6.6,6.6,6.6,6.5,6.4,6.4,6.2,6.0,5.9,5.7,5.7,5.6,5.5,5.4,5.4,5.4,5.4,5.3,5.2,5.0,4.9,4.8,4.7,4.6,4.4,4.3,4.3,4.3,4.3,4.3,4.2,4.0,4.0,4.0,3.9,3.8,3.8,3.7,3.7,3.6,3.6,3.6,3.5,3.4,3.3,3.2,3.0,3.0,3.0,2.8,2.8,2.7,2.5,2.4,2.3,2.3,2.3,2.2,2.1,2.1,2.0,2.0,1.8,1.7,1.6,1.5,1.5,1.5,1.4,1.3,1.3,1.2,1.2,1.1,1.1,1.0,1.0,0.9,0.9,0.9,0.7,0.7,0.7,0.7,0.7,0.6,0.6,0.6,0.5,0.5,0.4,0.3,0.3,0.2,0.2,0.2,0.1,0.1,0.1,0.1],"locations":["Belarus","Moldova","Lithuania","Russia","Romania","Ukraine","Andorra","Hungary","Czech Republic","Slovakia","Portugal","Serbia","Grenada","Poland","Latvia","Finland","South Korea","France","Australia","Croatia","Ireland","Luxembourg","Germany","Slovenia","United Kingdom","Denmark","Bulgaria","Spain","Belgium","South Africa","New Zealand","Gabon","Namibia","Switzerland","Saint Lucia","Austria","Estonia","Greece","Kazakhstan","Canada","Nigeria","Netherlands","Uganda","Rwanda","Chile","Argentina","Burundi","United States","Cyprus","Sweden","Venezuela","Paraguay","Brazil","Sierra Leone","Montenegro","Belize","Cameroon","Botswana","Saint Kitts and Nevis","Guyana","Peru","Panama","Niue","Palau","Norway","Tanzania","Georgia","Uruguay","Angola","Laos","Japan","Mexico","Ecuador","Dominica","Iceland","Thailand","Bosnia and Herzegovina","Sao Tome and Principe","Malta","Albania","Bahamas","Dominican Republic","Mongolia","Cape Verde","Barbados","Burkina Faso","Italy","Trinidad and Tobago","China","Macedonia","Saint Vincent and the Grenadines","Equatorial Guinea","Suriname","Vietnam","Lesotho","Haiti","Cook Islands","Colombia","Ivory Coast","Bolivia","Swaziland","Zimbabwe","Seychelles","Cambodia","Puerto Rico","Netherlands Antilles","Philippines","Costa Rica","Armenia","Cuba","Nicaragua","Jamaica","Ghana","Liberia","Uzbekistan","Chad","United Arab Emirates","Kyrgyzstan","India","Turkmenistan","Kenya","Ethiopia","Honduras","Guinea-Bissau","Zambia","Republic of the Congo","Guatemala","Central African Republic","North Korea","Sri Lanka","Mauritius","Samoa","Democratic Republic of the Congo","Nauru","Gambia","Federated States of Micronesia","El Salvador","Fiji","Papua New Guinea","Kiribati","Tajikistan","Israel","Sudan","Malawi","Lebanon","Azerbaijan","Mozambique","Togo","Nepal","Brunei","Benin","Singapore","Turkey","Madagascar","Solomon Islands","Tonga","Tunisia","Tuvalu","Qatar","Vanuatu","Djibouti","Malaysia","Syria","Maldives","Mali","Eritrea","Algeria","Iran","Oman","Brunei","Morocco","Jordan","Bhutan","Guinea","Burma","Afghanistan","Senegal","Indonesia","Timor-Leste","Iraq","Somalia","Egypt","Niger","Yemen","Comoros","Saudi Arabia","Bangladesh","Kuwait","Libya","Mauritania","Pakistan"],"locationmode":"country names","colorbar":{"len":0.5,"title":{"text":"Alcohol consumption[l/y]"}}}];""" - |> chartGeneratedContains choroplethMap2Chart - ); - testCase "Choropleth map 2 layout" ( fun () -> - "var layout = {\"geo\":{\"projection\":{\"type\":\"mollweide\"},\"showocean\":true,\"oceancolor\":\"lightblue\",\"showlakes\":true,\"showrivers\":true}};" - |> chartGeneratedContains choroplethMap2Chart - ); - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/MapboxMapCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/MapboxMapCharts.fs deleted file mode 100644 index d69f89051..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/MapboxMapCharts.fs +++ /dev/null @@ -1,220 +0,0 @@ -module Tests.MapboxMapCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open Plotly.NET.LayoutObjects -open System - -open TestUtils.HtmlCodegen - -let baseLayerOnlyChart = - Chart.PointMapbox( - longitudes = [], - latitudes = [], - UseDefaults = false - ) // deliberately empty chart to show the base map only - -[] -let ``Mapbox charts`` = - testList "MapboxMapCharts.Mapbox charts" [ - testCase "Base layout only data" ( fun () -> - """var data = [{"type":"scattermapbox","mode":"markers","lat":[],"lon":[],"cluster":{},"marker":{},"line":{}}];""" - |> chartGeneratedContains baseLayerOnlyChart - ); - testCase "Base layour only layout" ( fun () -> - "var layout = {\"mapbox\":{\"style\":\"open-street-map\"}};" - |> chartGeneratedContains baseLayerOnlyChart - ); - ] - - -let pointMapboxChart = - let cityNames = [ - "Montreal"; "Toronto"; "Vancouver"; "Calgary"; "Edmonton"; - "Ottawa"; "Halifax"; "Victoria"; "Winnepeg"; "Regina" - ] - - let lon = [ - -73.57; -79.24; -123.06; -114.1; -113.28; - -75.43; -63.57; -123.21; -97.13; -104.6 - ] - let lat = [ - 45.5; 43.4; 49.13; 51.1; 53.34; 45.24; - 44.64; 48.25; 49.89; 50.45 - ] - Chart.PointMapbox( - longitudes = lon, - latitudes = lat, - MultiText = cityNames, - TextPosition = StyleParam.TextPosition.TopCenter, - UseDefaults = false - ) - |> Chart.withMapbox( - Mapbox.init( - Center=(-104.6,50.45) - ) - ) - - -open Deedle -open FSharp.Data -open System.IO -open System.Text - - -let flightsChart = - let data = - "start_lat,start_lon,end_lat,end_lon,airline,airport1,airport2,cnt -32.89595056,-97.0372,35.04022222,-106.6091944,AA,DFW,ABQ,444 -41.979595,-87.90446417,30.19453278,-97.66987194,AA,ORD,AUS,166 -32.89595056,-97.0372,41.93887417,-72.68322833,AA,DFW,BDL,162 -18.43941667,-66.00183333,41.93887417,-72.68322833,AA,SJU,BDL,56 -32.89595056,-97.0372,33.56294306,-86.75354972,AA,DFW,BHM,168 -25.79325,-80.29055556,36.12447667,-86.67818222,AA,MIA,BNA,56 -32.89595056,-97.0372,42.3643475,-71.00517917,AA,DFW,BOS,422 -25.79325,-80.29055556,42.3643475,-71.00517917,AA,MIA,BOS,392" - |> fun csv -> Frame.ReadCsvString(csv,true,separators=",") - - let opacityVals : float [] = data.["cnt"] |> Series.values |> fun s -> s |> Seq.map (fun v -> v/(Seq.max s)) |> Array.ofSeq - let startCoords = Series.zipInner data.["start_lon"] data.["start_lat"] - let endCoords = Series.zipInner data.["end_lon"] data.["end_lat"] - let coords = Series.zipInner startCoords endCoords |> Series.values - - coords - |> Seq.mapi (fun i (startCoords,endCoords) -> - Chart.LineMapbox( - lonlat = [startCoords; endCoords], - Opacity = opacityVals.[i], - LineColor = Color.fromString "red", - UseDefaults = false - ) - ) - |> Chart.combine - |> Chart.withLegend(false) - |> Chart.withMapbox( - Mapbox.init( - Center=(-97.0372,32.8959) - ) - ) - |> Chart.withMarginSize(0,0,50,0) - |> Chart.withTitle "Feb. 2011 American Airline flights" - -[] -let ``Scatter and line plots on Mapbox maps charts`` = - testList "MapboxMapCharts.Scatter and line plots on Mapbox maps charts" [ - testCase "Point mapbox data" ( fun () -> - """var data = [{"type":"scattermapbox","mode":"markers+text","lat":[45.5,43.4,49.13,51.1,53.34,45.24,44.64,48.25,49.89,50.45],"lon":[-73.57,-79.24,-123.06,-114.1,-113.28,-75.43,-63.57,-123.21,-97.13,-104.6],"cluster":{},"text":["Montreal","Toronto","Vancouver","Calgary","Edmonton","Ottawa","Halifax","Victoria","Winnepeg","Regina"],"textposition":"top center","marker":{},"line":{}}];""" - |> chartGeneratedContains pointMapboxChart - ); - testCase "Point mapbox layout" ( fun () -> - "var layout = {\"mapbox\":{\"style\":\"open-street-map\",\"center\":{\"lon\":-104.6,\"lat\":50.45}}};" - |> chartGeneratedContains pointMapboxChart - ); - testCase "Flights mapbox data" ( fun () -> - """var data = [{"type":"scattermapbox","opacity":1.0,"mode":"lines","lat":[32.89595056,35.04022222],"lon":[-97.0372,-106.6091944],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.3738738738738739,"mode":"lines","lat":[41.979595,30.19453278],"lon":[-87.90446417,-97.66987194],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.36486486486486486,"mode":"lines","lat":[32.89595056,41.93887417],"lon":[-97.0372,-72.68322833],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.12612612612612611,"mode":"lines","lat":[18.43941667,41.93887417],"lon":[-66.00183333,-72.68322833],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.3783783783783784,"mode":"lines","lat":[32.89595056,33.56294306],"lon":[-97.0372,-86.75354972],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.12612612612612611,"mode":"lines","lat":[25.79325,36.12447667],"lon":[-80.29055556,-86.67818222],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.9504504504504504,"mode":"lines","lat":[32.89595056,42.3643475],"lon":[-97.0372,-71.00517917],"cluster":{},"marker":{},"line":{"color":"red"}},{"type":"scattermapbox","opacity":0.8828828828828829,"mode":"lines","lat":[25.79325,42.3643475],"lon":[-80.29055556,-71.00517917],"cluster":{},"marker":{},"line":{"color":"red"}}];""" - |> chartGeneratedContains flightsChart - ); - testCase "Flights mapbox layout" ( fun () -> - """var layout = {"mapbox":{"style":"open-street-map","center":{"lon":-97.0372,"lat":32.8959}},"showlegend":false,"margin":{"l":0,"r":0,"t":50,"b":0},"title":{"text":"Feb. 2011 American Airline flights"}};""" - |> chartGeneratedContains flightsChart - ); - ] - - -let densityMapboxChart = - let dataDensityMapbox = - "Date,Latitude,Longitude,Magnitude -01/02/1965,19.246,145.616,6.0 -01/04/1965,1.8630000000000002,127.352,5.8 -01/05/1965,-20.579,-173.972,6.2 -01/08/1965,-59.076,-23.557,5.8 -01/09/1965,11.937999999999999,126.427,5.8 -01/10/1965,-13.405,166.62900000000002,6.7 -01/12/1965,27.357,87.867,5.9 -01/15/1965,-13.309000000000001,166.21200000000002,6.0 -01/16/1965,-56.452,-27.043000000000003,6.0 -01/17/1965,-24.563000000000002,178.487,5.8 -01/17/1965,-6.807,108.988,5.9 -01/24/1965,-2.608,125.95200000000001,8.2 -01/29/1965,54.636,161.703,5.5 -02/01/1965,-18.697,-177.864,5.6 -02/02/1965,37.523,73.251,6.0 -02/04/1965,-51.84,139.741,6.1 -02/04/1965,51.251000000000005,178.715,8.7 -02/04/1965,51.638999999999996,175.055,6.0 -02/04/1965,52.528,172.007,5.7 -02/04/1965,51.626000000000005,175.74599999999998,5.8 -02/04/1965,51.037,177.84799999999998,5.9 -02/04/1965,51.73,173.975,5.9 -02/04/1965,51.775,173.058,5.7 -02/04/1965,52.611000000000004,172.588,5.7 -02/04/1965,51.831,174.368,5.7 -02/04/1965,51.948,173.96900000000002,5.6 -02/04/1965,51.443000000000005,179.605,7.3 -02/04/1965,52.773,171.97400000000002,6.5 -02/04/1965,51.772,174.696,5.6 -02/04/1965,52.975,171.09099999999998,6.4 -02/04/1965,52.99,170.87400000000002,5.8 -02/04/1965,51.536,175.045,5.8 -02/04/1965,13.245,-44.922,5.8 -02/04/1965,51.812,174.206,5.7 -02/05/1965,51.762,174.84099999999998,5.7 -02/05/1965,52.438,174.321,6.3 -02/05/1965,51.946000000000005,173.84,5.7 -02/05/1965,51.738,174.56599999999997,6.0 -02/05/1965,51.486999999999995,176.558,5.6 -02/06/1965,53.008,-162.00799999999998,6.4 -02/06/1965,52.184,175.505,6.2 -02/06/1965,52.076,172.918,5.6 -02/06/1965,51.744,175.213,5.7 -02/06/1965,52.056999999999995,174.11599999999999,5.7 -02/06/1965,53.191,-161.859,6.3 -02/06/1965,51.446999999999996,176.46900000000002,5.8 -02/07/1965,51.258,173.393,5.7 -02/07/1965,52.031000000000006,175.41099999999997,5.7 -02/07/1965,51.294,179.092,5.8 -02/08/1965,55.223,165.426,5.9 -02/09/1965,-18.718,169.386,5.6 -02/09/1965,52.815,171.90400000000002,6.0 -02/12/1965,52.188,172.752,5.8 -02/15/1965,51.00899999999999,179.325,5.8 -02/15/1965,3.0260000000000002,125.95100000000001,5.9 -02/16/1965,38.908,142.095,5.7 -02/17/1965,51.693999999999996,176.446,5.7 -02/17/1965,21.526999999999997,143.08100000000002,5.6 -02/18/1965,25.011,94.186,5.6" - |> fun d -> Frame.ReadCsvString(d,true,separators=",") - - let lon = dataDensityMapbox.["Longitude"] |> Series.values - let lat= dataDensityMapbox.["Latitude"] |> Series.values - let magnitudes = dataDensityMapbox.["Magnitude"] |> Series.values - Chart.DensityMapbox( - longitudes = lon, - latitudes = lat, - Z = magnitudes, - Radius=8, - ColorScale=StyleParam.Colorscale.Viridis, - UseDefaults = false - ) - |> Chart.withMapbox( - Mapbox.init( - Style = StyleParam.MapboxStyle.StamenTerrain, - Center = (60.,30.) - ) - ) - -[] -let ``DensityMapbox charts`` = - testList "MapboxMapCharts.DensityMapbox charts charts" [ - testCase "Density Mapbox data" ( fun () -> - """var data = [{"type":"densitymapbox","z":[6.0,5.8,6.2,5.8,5.8,6.7,5.9,6.0,6.0,5.8,5.9,8.2,5.5,5.6,6.0,6.1,8.7,6.0,5.7,5.8,5.9,5.9,5.7,5.7,5.7,5.6,7.3,6.5,5.6,6.4,5.8,5.8,5.8,5.7,5.7,6.3,5.7,6.0,5.6,6.4,6.2,5.6,5.7,5.7,6.3,5.8,5.7,5.7,5.8,5.9,5.6,6.0,5.8,5.8,5.9,5.7,5.7,5.6,5.6],"radius":8,"lat":[19.246,1.863,-20.579,-59.076,11.938,-13.405,27.357,-13.309,-56.452,-24.563,-6.807,-2.608,54.636,-18.697,37.523,-51.84,51.251000000000005,51.639,52.528,51.626000000000005,51.037,51.73,51.775,52.611,51.831,51.948,51.443000000000005,52.773,51.772,52.975,52.99,51.536,13.245,51.812,51.762,52.438,51.946000000000005,51.738,51.486999999999995,53.008,52.184,52.076,51.744,52.056999999999995,53.191,51.447,51.258,52.031000000000006,51.294,55.223,-18.718,52.815,52.188,51.00899999999999,3.026,38.908,51.694,21.526999999999997,25.011],"lon":[145.616,127.352,-173.972,-23.557,126.427,166.62900000000002,87.867,166.21200000000002,-27.043000000000003,178.487,108.988,125.952,161.703,-177.864,73.251,139.741,178.715,175.055,172.007,175.74599999999998,177.84799999999998,173.975,173.058,172.588,174.368,173.96900000000002,179.605,171.97400000000002,174.696,171.09099999999998,170.87400000000002,175.045,-44.922,174.206,174.84099999999998,174.321,173.84,174.56599999999997,176.558,-162.00799999999998,175.505,172.918,175.213,174.116,-161.859,176.46900000000002,173.393,175.41099999999997,179.092,165.426,169.386,171.90400000000002,172.752,179.325,125.951,142.095,176.446,143.08100000000002,94.186],"colorscale":"Viridis"}];""" - |> chartGeneratedContains densityMapboxChart - ); - testCase "Density Mapbox layout" ( fun () -> - "var layout = {\"mapbox\":{\"style\":\"stamen-terrain\",\"center\":{\"lon\":60.0,\"lat\":30.0}}};" - |> chartGeneratedContains densityMapboxChart - ); - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/PolarCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/PolarCharts.fs deleted file mode 100644 index 8e571af3f..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/PolarCharts.fs +++ /dev/null @@ -1,116 +0,0 @@ -module Tests.PolarCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open System - -open TestUtils.HtmlCodegen - -//radial coordinates -let radial = [ 1; 2; 3; 4; 5; 6; 7;] -// angular coordinates -let theta = [0; 45; 90; 135; 200; 320; 184;] - - -let pointPolar = Chart.PointPolar(r = radial,theta = theta, UseDefaults = false) - -let linePolar = - Chart.LinePolar(r = radial,theta = theta, UseDefaults = false) - |> Chart.withLineStyle(Color=Color.fromString "purple",Dash=StyleParam.DrawingStyle.DashDot) - -let splinePolar = - Chart.SplinePolar( - r = radial, - theta = theta, - MultiText=["one";"two";"three";"four";"five";"six";"seven"], - TextPosition=StyleParam.TextPosition.TopCenter, - ShowMarkers=true, - UseDefaults = false - ) - -let barPolar = - let r = [77.5; 72.5; 70.0; 45.0; 22.5; 42.5; 40.0; 62.5] - let r2 = [57.5; 50.0; 45.0; 35.0; 20.0; 22.5; 37.5; 55.0] - let r3 = [40.0; 30.0; 30.0; 35.0; 7.5; 7.5; 32.5; 40.0] - let r4 = [20.0; 7.5; 15.0; 22.5; 2.5; 2.5; 12.5; 22.5] - - let t = ["North"; "N-E"; "East"; "S-E"; "South"; "S-W"; "West"; "N-W"] - - [ - Chart.BarPolar (r = r , theta = t, Name="11-14 m/s" , MarkerPatternShape = StyleParam.PatternShape.Checked , UseDefaults = false) - Chart.BarPolar (r = r2, theta = t, Name="8-11 m/s" , MarkerPatternShape = StyleParam.PatternShape.DiagonalChecked, UseDefaults = false) - Chart.BarPolar (r = r3, theta = t, Name="5-8 m/s" , MarkerPatternShape = StyleParam.PatternShape.VerticalLines , UseDefaults = false) - Chart.BarPolar (r = r4, theta = t, Name="< 5 m/s" , MarkerPatternShape = StyleParam.PatternShape.HorizontalLines, UseDefaults = false) - ] - |> Chart.combine - |> Chart.withAngularAxis( - AngularAxis.init( - CategoryOrder = StyleParam.CategoryOrder.Array, - CategoryArray = (["East"; "N-E"; "North"; "N-W"; "West"; "S-W"; "South"; "S-E";]) // set the order of the categorical axis - ) - ) - -[] -let ``Polar charts`` = - testList "PolarCharts.Polar charts" [ - testCase "Polar Point data" ( fun () -> - """var data = [{"type":"scatterpolar","mode":"markers","r":[1,2,3,4,5,6,7],"theta":[0,45,90,135,200,320,184],"marker":{}}];""" - |> chartGeneratedContains pointPolar - ); - testCase "Polar Point layout" ( fun () -> - emptyLayout pointPolar - ); - testCase "Polar Line data" ( fun () -> - """var data = [{"type":"scatterpolar","mode":"lines","r":[1,2,3,4,5,6,7],"theta":[0,45,90,135,200,320,184],"marker":{},"line":{"color":"purple","dash":"dashdot"}}];""" - |> chartGeneratedContains linePolar - ); - testCase "Polar Line layout" ( fun () -> - emptyLayout linePolar - ); - testCase "Polar Spline data" ( fun () -> - """var data = [{"type":"scatterpolar","mode":"lines+markers+text","r":[1,2,3,4,5,6,7],"theta":[0,45,90,135,200,320,184],"text":["one","two","three","four","five","six","seven"],"textposition":"top center","marker":{},"line":{"shape":"spline"}}];""" - |> chartGeneratedContains splinePolar - ); - testCase "Polar Spline layout" ( fun () -> - emptyLayout splinePolar - ); - testCase "Polar Bar data" ( fun () -> - """var data = [{"type":"barpolar","name":"11-14 m/s","r":[77.5,72.5,70.0,45.0,22.5,42.5,40.0,62.5],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"+"}}},{"type":"barpolar","name":"8-11 m/s","r":[57.5,50.0,45.0,35.0,20.0,22.5,37.5,55.0],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"x"}}},{"type":"barpolar","name":"5-8 m/s","r":[40.0,30.0,30.0,35.0,7.5,7.5,32.5,40.0],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"|"}}},{"type":"barpolar","name":"< 5 m/s","r":[20.0,7.5,15.0,22.5,2.5,2.5,12.5,22.5],"theta":["North","N-E","East","S-E","South","S-W","West","N-W"],"marker":{"pattern":{"shape":"-"}}}];""" - |> chartGeneratedContains barPolar - ); - testCase "Polar Bar layout" ( fun () -> - """var layout = {"polar":{"angularaxis":{"categoryorder":"array","categoryarray":["East","N-E","North","N-W","West","S-W","South","S-E"]}}};""" - |> chartGeneratedContains barPolar - ); - ] - - -//let windrose1Chart = -// let r = [77.5; 72.5; 70.0; 45.0; 22.5; 42.5; 40.0; 62.5] -// let r' = [57.5; 50.0; 45.0; 35.0; 20.0; 22.5; 37.5; 55.0] -// let r'' = [40.0; 30.0; 30.0; 35.0; 7.5; 7.5; 32.5; 40.0] -// let r''' = [20.0; 7.5; 15.0; 22.5; 2.5; 2.5; 12.5; 22.5] - -// let t = ["North"; "N-E"; "East"; "S-E"; "South"; "S-W"; "West"; "N-W"] -// [ -// Chart.WindRose (r ,t,Name="11-14 m/s") -// Chart.WindRose (r' ,t,Name="8-11 m/s") -// Chart.WindRose (r'' ,t,Name="5-8 m/s") -// Chart.WindRose (r''',t,Name="< 5 m/s") -// ] -// |> Chart.combine - -//[] -//let ``Windrose charts`` = -// testList "PolarCharts.Windrose charts" [ -// testCase "Windrose data" ( fun () -> -// "var data = [{\"type\":\"area\",\"r\":[77.5,72.5,70.0,45.0,22.5,42.5,40.0,62.5],\"t\":[\"North\",\"N-E\",\"East\",\"S-E\",\"South\",\"S-W\",\"West\",\"N-W\"],\"name\":\"11-14 m/s\",\"line\":{},\"marker\":{}},{\"type\":\"area\",\"r\":[57.5,50.0,45.0,35.0,20.0,22.5,37.5,55.0],\"t\":[\"North\",\"N-E\",\"East\",\"S-E\",\"South\",\"S-W\",\"West\",\"N-W\"],\"name\":\"8-11 m/s\",\"line\":{},\"marker\":{}},{\"type\":\"area\",\"r\":[40.0,30.0,30.0,35.0,7.5,7.5,32.5,40.0],\"t\":[\"North\",\"N-E\",\"East\",\"S-E\",\"South\",\"S-W\",\"West\",\"N-W\"],\"name\":\"5-8 m/s\",\"line\":{},\"marker\":{}},{\"type\":\"area\",\"r\":[20.0,7.5,15.0,22.5,2.5,2.5,12.5,22.5],\"t\":[\"North\",\"N-E\",\"East\",\"S-E\",\"South\",\"S-W\",\"West\",\"N-W\"],\"name\":\"< 5 m/s\",\"line\":{},\"marker\":{}}];" -// |> chartGeneratedContains windrose1Chart -// ); -// testCase "Windrose layout" ( fun () -> -// emptyLayout windrose1Chart -// ); -// ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/SimpleCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/SimpleCharts.fs deleted file mode 100644 index 29a89654e..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/SimpleCharts.fs +++ /dev/null @@ -1,658 +0,0 @@ -module Tests.SimpleCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open Plotly.NET.StyleParam - -open TestUtils.HtmlCodegen - -let withLineStyleChart = - let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - Chart.Line( - x = x, - y = y, - Name="line", - ShowMarkers=true, - MarkerSymbol=StyleParam.MarkerSymbol.Square, - UseDefaults = false - ) - |> Chart.withLineStyle(Width=2.,Dash=StyleParam.DrawingStyle.Dot) - - -let chartLineChart = Chart.Line([ for x in 1.0 .. 100.0 -> (x, x ** 2.0) ], UseDefaults = false) - -let splineChart = - let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - Chart.Spline(x = x, y = y, Name="spline", UseDefaults = false) - -let textLabelChart = - let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - let labels = ["a";"b";"c";"d";"e";"f";"g";"h";"i";"j";] - Chart.Point( - x = x,y = y, - Name="points", - MultiText=labels, - TextPosition=StyleParam.TextPosition.TopRight, - UseDefaults = false - ) - - -[] -let ``Line and scatter plots`` = - testList "SimpleCharts.Line and scatter plots" [ - testCase "With LineStyle data" ( fun () -> - """var data = [{"type":"scatter","name":"line","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{"symbol":"1"},"line":{"width":2.0,"dash":"dot"}}];""" - |> chartGeneratedContains withLineStyleChart - ); - testCase "With LineStyle layout" ( fun () -> - emptyLayout withLineStyleChart - ); - testCase "Chart line data" ( fun () -> - """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0,65.0,66.0,67.0,68.0,69.0,70.0,71.0,72.0,73.0,74.0,75.0,76.0,77.0,78.0,79.0,80.0,81.0,82.0,83.0,84.0,85.0,86.0,87.0,88.0,89.0,90.0,91.0,92.0,93.0,94.0,95.0,96.0,97.0,98.0,99.0,100.0],"y":[1.0,4.0,9.0,16.0,25.0,36.0,49.0,64.0,81.0,100.0,121.0,144.0,169.0,196.0,225.0,256.0,289.0,324.0,361.0,400.0,441.0,484.0,529.0,576.0,625.0,676.0,729.0,784.0,841.0,900.0,961.0,1024.0,1089.0,1156.0,1225.0,1296.0,1369.0,1444.0,1521.0,1600.0,1681.0,1764.0,1849.0,1936.0,2025.0,2116.0,2209.0,2304.0,2401.0,2500.0,2601.0,2704.0,2809.0,2916.0,3025.0,3136.0,3249.0,3364.0,3481.0,3600.0,3721.0,3844.0,3969.0,4096.0,4225.0,4356.0,4489.0,4624.0,4761.0,4900.0,5041.0,5184.0,5329.0,5476.0,5625.0,5776.0,5929.0,6084.0,6241.0,6400.0,6561.0,6724.0,6889.0,7056.0,7225.0,7396.0,7569.0,7744.0,7921.0,8100.0,8281.0,8464.0,8649.0,8836.0,9025.0,9216.0,9409.0,9604.0,9801.0,10000.0],"marker":{},"line":{}}];""" - |> chartGeneratedContains chartLineChart - ); - testCase "Chart line layout" ( fun () -> - emptyLayout chartLineChart - ); - testCase "Spline chart data" ( fun () -> - """var data = [{"type":"scatter","name":"spline","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{},"line":{"shape":"spline"}}];""" - |> chartGeneratedContains splineChart - ); - testCase "Spline chart layout" ( fun () -> - emptyLayout splineChart - ); - testCase "Text label data" ( fun () -> - """var data = [{"type":"scatter","name":"points","mode":"markers+text","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"text":["a","b","c","d","e","f","g","h","i","j"],"textposition":"top right","marker":{},"line":{}}];""" - |> chartGeneratedContains textLabelChart - ); - testCase "Text label layout" ( fun () -> - emptyLayout textLabelChart - ); - ] - - -let columnChart = - let values = [20; 14; 23;] - let keys = ["Product A"; "Product B"; "Product C";] - Chart.Column(values = values, Keys = keys, UseDefaults = false) - -let barChart = - let values = [20; 14; 23;] - let keys = ["Product A"; "Product B"; "Product C";] - Chart.Bar(values = values, Keys = keys, UseDefaults = false) - -let stackedBarChart = - let values = [20; 14; 23;] - let keys = ["Product A"; "Product B"; "Product C";] - [ - Chart.StackedBar(values = values, Keys = keys, Name="old", UseDefaults = false); - Chart.StackedBar(values = [8; 21; 13;], Keys = keys, Name="new", UseDefaults = false) - ] - |> Chart.combine - -let stackedColumnChart = - let values = [20; 14; 23;] - let keys = ["Product A"; "Product B"; "Product C";] - [ - Chart.StackedColumn(values = values, Keys = keys,Name="old", UseDefaults = false); - Chart.StackedColumn(values = [8; 21; 13;], Keys = keys,Name="new", UseDefaults = false) - ] - |> Chart.combine - -[] -let ``Bar and column charts`` = - testList "SimpleCharts.Bar and column charts" [ - testCase "Column chart data" ( fun () -> - """var data = [{"type":"bar","x":["Product A","Product B","Product C"],"y":[20,14,23],"orientation":"v","marker":{"pattern":{}}}];""" - |> chartGeneratedContains columnChart - ); - testCase "Column chart layout" ( fun () -> - emptyLayout columnChart - ); - testCase "Bar chart data" ( fun () -> - """var data = [{"type":"bar","x":[20,14,23],"y":["Product A","Product B","Product C"],"orientation":"h","marker":{"pattern":{}}}];""" - |> chartGeneratedContains barChart - ); - testCase "Bar chart layout" ( fun () -> - emptyLayout barChart - ); - testCase "Stacked bar data" ( fun () -> - """var data = [{"type":"bar","name":"old","x":[20,14,23],"y":["Product A","Product B","Product C"],"orientation":"h","marker":{"pattern":{}}},{"type":"bar","name":"new","x":[8,21,13],"y":["Product A","Product B","Product C"],"orientation":"h","marker":{"pattern":{}}}];""" - |> chartGeneratedContains stackedBarChart - ); - testCase "Stacked bar layout" ( fun () -> - "var layout = {\"barmode\":\"stack\"};" - |> chartGeneratedContains stackedColumnChart - ); - testCase "Stacked column data" ( fun () -> - """var data = [{"type":"bar","name":"old","x":["Product A","Product B","Product C"],"y":[20,14,23],"orientation":"v","marker":{"pattern":{}}},{"type":"bar","name":"new","x":["Product A","Product B","Product C"],"y":[8,21,13],"orientation":"v","marker":{"pattern":{}}}];""" - |> chartGeneratedContains stackedColumnChart - ); - testCase "Stacked column layout" ( fun () -> - "var layout = {\"barmode\":\"stack\"};" - |> chartGeneratedContains stackedColumnChart - ); - ] - - -let simpleAreaChart = - let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] - let y = [5.; 2.5; 5.; 7.5; 5.; 2.5; 7.5; 4.5; 5.5; 5.] - Chart.Area(x = x, y = y, UseDefaults = false) - -let withSplineChart = - let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] - let y = [5.; 2.5; 5.; 7.5; 5.; 2.5; 7.5; 4.5; 5.5; 5.] - Chart.SplineArea(x = x, y = y, UseDefaults = false) - -let stackedAreaChart = - let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] - let y = [5.; 2.5; 5.; 7.5; 5.; 2.5; 7.5; 4.5; 5.5; 5.] - [ - Chart.StackedArea(x = x, y = y, UseDefaults = false) - Chart.StackedArea(x = x, y = (y |> Seq.rev), UseDefaults = false) - ] - |> Chart.combine - -[] -let ``Area charts`` = - testList "SimpleCharts.Area charts" [ - testCase "Simple area data" ( fun () -> - """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,2.5,5.0,7.5,5.0,2.5,7.5,4.5,5.5,5.0],"marker":{},"line":{},"fill":"tozeroy","fillpattern":{}}];""" - |> chartGeneratedContains simpleAreaChart - ); - testCase "Simple area layout" ( fun () -> - emptyLayout simpleAreaChart - ); - testCase "Spline data" ( fun () -> - """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,2.5,5.0,7.5,5.0,2.5,7.5,4.5,5.5,5.0],"marker":{},"line":{"shape":"spline"},"fill":"tozeroy","fillpattern":{}}];""" - |> chartGeneratedContains withSplineChart - ); - testCase "Spline layout" ( fun () -> - emptyLayout withSplineChart - ); - testCase "Stacked area data" ( fun () -> - """var data = [{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,2.5,5.0,7.5,5.0,2.5,7.5,4.5,5.5,5.0],"stackgroup":"stackedarea","marker":{},"line":{},"fill":"tonexty","fillpattern":{}},{"type":"scatter","mode":"lines","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[5.0,5.5,4.5,7.5,2.5,5.0,7.5,5.0,2.5,5.0],"stackgroup":"stackedarea","marker":{},"line":{},"fill":"tonexty","fillpattern":{}}];""" - |> chartGeneratedContains stackedAreaChart - ); - testCase "Stacked area layout" ( fun () -> - emptyLayout stackedAreaChart - ); - ] - - -let rangePlotsChart = - let rnd = System.Random(5) - - let x = [1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10.; ] - let y = [2.; 1.5; 5.; 1.5; 3.; 2.5; 2.5; 1.5; 3.5; 1.] - - let yUpper = y |> List.map (fun v -> v + rnd.NextDouble()) - let yLower = y |> List.map (fun v -> v - rnd.NextDouble()) - Chart.Range( - x = x, - y = y, - upper = yUpper, - lower = yLower, - mode = StyleParam.Mode.Lines_Markers, - MarkerColor=Color.fromString "grey", - RangeColor=Color.fromString "lightblue", - UseDefaults = false) - -[] -let ``Range plot`` = - testList "SimpleCharts.Range plot" [ - testCase "Range plot data" ( fun () -> - """var data = [{"type":"scatter","name":"lower","showlegend":true,"legendgroup":"Range","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[1.0244410755226578,1.1291130737537114,4.930085632917511,1.4292117752736488,2.5179894182449156,2.3470285278032668,1.5358344954605374,1.4046562835130172,2.6874669190437843,0.7493837949584163],"fillcolor":"lightblue","marker":{"color":"lightblue"}},{"type":"scatter","name":"upper","showlegend":true,"legendgroup":"Range","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.338369840913624,1.7844184475412679,5.2629626417825754,2.125375844363764,3.4634618528482792,3.4283738280312965,2.6463105539541276,2.4505998873853123,4.096133255211699,1.1174599459010455],"fill":"tonexty","fillcolor":"lightblue","marker":{"color":"lightblue"}},{"type":"scatter","mode":"lines+markers","x":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0],"y":[2.0,1.5,5.0,1.5,3.0,2.5,2.5,1.5,3.5,1.0],"marker":{"color":"grey"},"line":{},"legendgroup":"Range","legendgrouptitle":{"text":"Range"}}];""" - |> chartGeneratedContains rangePlotsChart - ); - testCase "Range plot layout" ( fun () -> - emptyLayout rangePlotsChart - ); - ] - -let bubbleCharts = - let x = [2; 4; 6;] - let y = [4; 1; 6;] - let size = [19; 26; 55;] - Chart.Bubble(x = x, y = y,sizes = size, UseDefaults = false) - -[] -let ``Bubble charts`` = - testList "SimpleCharts.Bubble charts" [ - testCase "Bubble data" ( fun () -> - """var data = [{"type":"scatter","mode":"markers","x":[2,4,6],"y":[4,1,6],"marker":{"size":[19,26,55]},"line":{}}];""" - |> chartGeneratedContains bubbleCharts - ); - testCase "Bubble layout" ( fun () -> - emptyLayout bubbleCharts - ); - ] - -let pieChart = - let values = [19; 26; 55;] - let labels = ["Residential"; "Non-Residential"; "Utility"] - Chart.Pie(values = values, Labels = labels, UseDefaults = false) - -let pieStyled = - - let values = [19; 26; 55;] - let labels = ["Residential"; "Non-Residential"; "Utility"] - - Chart.Pie( - values = values, - Labels = labels, - SectionColors = [ - Color.fromKeyword Aqua - Color.fromKeyword Salmon - Color.fromKeyword Tan - ], - SectionOutlineColor = Color.fromKeyword Black, - SectionOutlineWidth = 2., - MultiText = [ - "Some" - "More" - "Stuff" - ], - MultiTextPosition = [ - StyleParam.TextPosition.Inside - StyleParam.TextPosition.Outside - StyleParam.TextPosition.Inside - ], - Rotation = 45., - MultiPull = [0.; 0.3; 0.], - UseDefaults = false - ) - -let doughnutChart = - let values = [19; 26; 55;] - let labels = ["Residential"; "Non-Residential"; "Utility"] - Chart.Doughnut( - values = values, - Labels = labels, - Hole=0.3, - MultiText=labels, - UseDefaults = false - ) - -[] -let ``Pie and doughnut Charts`` = - testList "SimpleCharts.Pie and doughnut Charts" [ - testCase "Pie data" ( fun () -> - """var data = [{"type":"pie","values":[19,26,55],"labels":["Residential","Non-Residential","Utility"],"marker":{"line":{}}}];""" - |> chartGeneratedContains pieChart - ); - testCase "Pie layout" ( fun () -> - emptyLayout pieChart - ); - testCase "Pie styled data" ( fun () -> - """var data = [{"type":"pie","values":[19,26,55],"labels":["Residential","Non-Residential","Utility"],"pull":[0.0,0.3,0.0],"text":["Some","More","Stuff"],"textposition":["inside","outside","inside"],"marker":{"colors":["rgba(0, 255, 255, 1.0)","rgba(250, 128, 114, 1.0)","rgba(210, 180, 140, 1.0)"],"line":{"color":"rgba(0, 0, 0, 1.0)","width":2.0}},"rotation":45.0}];""" - |> chartGeneratedContains pieStyled - ); - testCase "Pie styled layout" ( fun () -> - emptyLayout pieStyled - ); - testCase "Doughnut data" ( fun () -> - """var data = [{"type":"pie","values":[19,26,55],"labels":["Residential","Non-Residential","Utility"],"text":["Residential","Non-Residential","Utility"],"marker":{"line":{}},"hole":0.3}];""" - |> chartGeneratedContains doughnutChart - ); - testCase "Doughnut layout" ( fun () -> - emptyLayout doughnutChart - ); - ] - - -let table1Chart = - let header = ["RowIndex";"A";"simple";"table"] - let rows = - [ - ["0";"I" ;"am" ;"a"] - ["1";"little";"example";"!"] - ] - Chart.Table(headerValues = header, cellsValues = rows, UseDefaults = false) - -let tableStyledChart = - let header = ["RowIndex";"A";"simple";"table"] - let rows = - [ - ["0";"I" ;"am" ;"a"] - ["1";"little";"example";"!"] - ] - Chart.Table( - headerValues = header, - cellsValues = rows, - HeaderAlign = HorizontalAlign.Center, - CellsMultiAlign = [HorizontalAlign.Left; HorizontalAlign.Center; HorizontalAlign.Right], - HeaderFillColor = Color.fromString "#45546a", - CellsFillColor = Color.fromColors [Color.fromString "#deebf7"; Color.fromString "lightgrey"; Color.fromString "#deebf7"; Color.fromString "lightgrey"], - HeaderHeight = 30, - HeaderOutlineColor = Color.fromString "black", - HeaderOutlineWidth = 2., - MultiColumnWidth = [70.; 50.; 100.; 70.], - ColumnOrder = [1; 2; 3; 4], - UseDefaults = false - ) - -let tableColorDependentChart = - let header2 = ["Identifier";"T0";"T1";"T2";"T3"] - let rowvalues = - [ - [10001.;0.2;2.0;4.0;5.0] - [10002.;2.1;2.0;1.8;2.1] - [10003.;4.5;3.0;2.0;2.5] - [10004.;0.0;0.1;0.3;0.2] - [10005.;1.0;1.6;1.8;2.2] - [10006.;1.0;0.8;1.5;0.7] - [10007.;2.0;2.0;2.1;1.9] - ] - |> Seq.sortBy (fun x -> x.[1]) - - //map color from value to hex representation - let mapColor min max value = - let proportion = - (255. * (value - min) / (max - min)) - |> int - Color.fromRGB 255 (255 - proportion) proportion - - //Assign a color to every cell seperately. Matrix must be transposed for correct orientation. - let cellcolor = - rowvalues - |> Seq.map (fun row -> - row - |> Seq.mapi (fun index value -> - if index = 0 then Color.fromString "white" - else mapColor 0. 5. value - ) - ) - |> Seq.transpose - |> Seq.map Color.fromColors - |> Color.fromColors - - Chart.Table( - headerValues = header2, - cellsValues = rowvalues, - CellsFillColor=cellcolor, - UseDefaults = false - ) - -let sequencePresentationTableChart = - let sequence = - [ - "ATGAGACGTCGAGACTGATAGACGTCGATAGACGTCGATAGACCG" - "ATAGACTCGTGATAGACGTCGATAGACGTCGATAGAGTATAGACC" - "GTGATAGACGTCGAGAAGACGTCGATAGACGTCGATAGACGTCGA" - "TAGAGATAGACGTCGATAGACCGTATAGAAGACGTCGATAGATAG" - "ACGTCGATAGACCGTAGACGTCGATAGACGTCGATAGACCGT" - ] - |> String.concat "" - - let elementsPerRow = 60 - - let headers = - [0..elementsPerRow] - |> Seq.map (fun x -> - if x%10=0 && x <> 0 then "|" - else "" - ) - - let cells = - sequence - |> Seq.chunkBySize elementsPerRow - |> Seq.mapi (fun i x -> Seq.append [string (i * elementsPerRow)] (Seq.map string x)) - - let cellcolors = - cells - |> Seq.map (fun row -> - row - |> Seq.map (fun element -> - match element with - //colors taken from DRuMS - //(http://biomodel.uah.es/en/model4/dna/atgc.htm) - | "A" -> Color.fromString "#5050FF" - | "T" -> Color.fromString "#E6E600" - | "G" -> Color.fromString "#00C000" - | "C" -> Color.fromString "#E00000" - | "U" -> Color.fromString "#B48100" - | _ -> Color.fromString "white" - ) - ) - |> Seq.transpose - |> Seq.map (fun x -> Seq.append x (seq [Color.fromString "white"])) - |> Seq.map Color.fromColors - |> Color.fromColors - - let line = Line.init(Width = 0., Color = Color.fromString "white") - let chartwidth = 50 + 10 * elementsPerRow - - Chart.Table( - headerValues = headers, - cellsValues = cells, - CellsOutline = line, - HeaderOutline = line, - CellsHeight = 20, - MultiColumnWidth = [50.;10.], - CellsMultiAlign = [HorizontalAlign.Right;HorizontalAlign.Center], - CellsFillColor = cellcolors, - UseDefaults = false - ) - |> Chart.withSize(Width=chartwidth) - |> Chart.withTitle "Sequence A" - - -[] -let ``Table charts`` = - testList "SimpleCharts.Table charts" [ - testCase "First table data" ( fun () -> - """var data = [{"type":"table","cells":{"fill":{},"line":{},"values":[["0","1"],["I","little"],["am","example"],["a","!"]]},"header":{"fill":{},"line":{},"values":["RowIndex","A","simple","table"]}}];""" - |> chartGeneratedContains table1Chart - ); - testCase "First table layout" ( fun () -> - emptyLayout table1Chart - ); - testCase "Styled table data" ( fun () -> - """var data = [{"type":"table","columnorder":[1,2,3,4],"columnwidth":[70.0,50.0,100.0,70.0],"cells":{"align":["left","center","right"],"fill":{"color":["#deebf7","lightgrey","#deebf7","lightgrey"]},"line":{},"values":[["0","1"],["I","little"],["am","example"],["a","!"]]},"header":{"align":"center","fill":{"color":"#45546a"},"height":30,"line":{"color":"black","width":2.0},"values":["RowIndex","A","simple","table"]}}];""" - |> chartGeneratedContains tableStyledChart - ); - testCase "Styled table layout" ( fun () -> - emptyLayout tableStyledChart - ); - testCase "Color dependent chart data" ( fun () -> - """var data = [{"type":"table","cells":{"fill":{"color":[["white","white","white","white","white","white","white"],["rgba(255, 255, 0, 1.0)","rgba(255, 245, 10, 1.0)","rgba(255, 204, 51, 1.0)","rgba(255, 204, 51, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 148, 107, 1.0)","rgba(255, 26, 229, 1.0)"],["rgba(255, 250, 5, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 174, 81, 1.0)","rgba(255, 215, 40, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 153, 102, 1.0)","rgba(255, 102, 153, 1.0)"],["rgba(255, 240, 15, 1.0)","rgba(255, 51, 204, 1.0)","rgba(255, 164, 91, 1.0)","rgba(255, 179, 76, 1.0)","rgba(255, 148, 107, 1.0)","rgba(255, 164, 91, 1.0)","rgba(255, 153, 102, 1.0)"],["rgba(255, 245, 10, 1.0)","rgba(255, 0, 255, 1.0)","rgba(255, 143, 112, 1.0)","rgba(255, 220, 35, 1.0)","rgba(255, 159, 96, 1.0)","rgba(255, 148, 107, 1.0)","rgba(255, 128, 127, 1.0)"]]},"line":{},"values":[[10004.0,10001.0,10005.0,10006.0,10007.0,10002.0,10003.0],[0.0,0.2,1.0,1.0,2.0,2.1,4.5],[0.1,2.0,1.6,0.8,2.0,2.0,3.0],[0.3,4.0,1.8,1.5,2.1,1.8,2.0],[0.2,5.0,2.2,0.7,1.9,2.1,2.5]]},"header":{"fill":{},"line":{},"values":["Identifier","T0","T1","T2","T3"]}}];""" - |> chartGeneratedContains tableColorDependentChart - ); - testCase "Color dependent chart layout" ( fun () -> - emptyLayout tableColorDependentChart - ); - testCase "Sequence presentation table data" ( fun () -> - """var data = [{"type":"table","columnwidth":[50.0,10.0],"cells":{"align":["right","center"],"fill":{"color":[["white","white","white","white","white"],["#5050FF","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E00000","#E6E600","#E00000","white"],["#00C000","#00C000","#E00000","#00C000","white"],["#5050FF","#E6E600","#00C000","#E6E600","white"],["#00C000","#E00000","#5050FF","#E00000","white"],["#5050FF","#00C000","#E6E600","#00C000","white"],["#E00000","#5050FF","#5050FF","#5050FF","white"],["#00C000","#E6E600","#00C000","#E6E600","white"],["#E6E600","#5050FF","#5050FF","#5050FF","white"],["#E00000","#00C000","#E00000","#00C000","white"],["#00C000","#5050FF","#00C000","#5050FF","white"],["#5050FF","#E00000","#E6E600","#E00000","white"],["#00C000","#00C000","#E00000","#E00000","white"],["#5050FF","#E6E600","#00C000","#00C000","white"],["#E00000","#E00000","#5050FF","#E6E600","white"],["#E6E600","#00C000","#E6E600","#5050FF","white"],["#00C000","#5050FF","#5050FF","#00C000","white"],["#5050FF","#E6E600","#00C000","#5050FF","white"],["#E6E600","#5050FF","#5050FF","#E00000","white"],["#5050FF","#00C000","#00C000","#00C000","white"],["#00C000","#5050FF","#5050FF","#E6E600","white"],["#5050FF","#00C000","#E6E600","#E00000","white"],["#E00000","#E6E600","#5050FF","#00C000","white"],["#00C000","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E6E600","#5050FF","#E6E600","white"],["#E00000","#5050FF","#E00000","#5050FF","white"],["#00C000","#00C000","#00C000","#00C000","white"],["#5050FF","#5050FF","#E6E600","#5050FF","white"],["#E6E600","#E00000","#E00000","#E00000","white"],["#5050FF","#E00000","#00C000","#00C000","white"],["#00C000","#00C000","#5050FF","#E6E600","white"],["#5050FF","#E6E600","#E6E600","#E00000","white"],["#E00000","#00C000","#5050FF","#00C000","white"],["#00C000","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E6E600","#5050FF","#E6E600","white"],["#E00000","#5050FF","#E00000","#5050FF","white"],["#00C000","#00C000","#E00000","#00C000","white"],["#5050FF","#5050FF","#00C000","#5050FF","white"],["#E6E600","#E00000","#E6E600","#E00000","white"],["#5050FF","#00C000","#5050FF","#E00000","white"],["#00C000","#E6E600","#E6E600","#00C000","white"],["#5050FF","#E00000","#5050FF","#E6E600","white"],["#E00000","#00C000","#00C000","white"],["#E00000","#5050FF","#5050FF","white"],["#00C000","#00C000","#5050FF","white"],["#5050FF","#5050FF","#00C000","white"],["#E6E600","#5050FF","#5050FF","white"],["#5050FF","#00C000","#E00000","white"],["#00C000","#5050FF","#00C000","white"],["#5050FF","#E00000","#E6E600","white"],["#E00000","#00C000","#E00000","white"],["#E6E600","#E6E600","#00C000","white"],["#E00000","#E00000","#5050FF","white"],["#00C000","#00C000","#E6E600","white"],["#E6E600","#5050FF","#5050FF","white"],["#00C000","#E6E600","#00C000","white"],["#5050FF","#5050FF","#5050FF","white"],["#E6E600","#00C000","#E6E600","white"],["#5050FF","#5050FF","#5050FF","white"],["#00C000","#E00000","#00C000","white"]]},"height":20,"line":{"color":"white","width":0.0},"values":[["0","60","120","180"],["A","A","G","A"],["T","C","T","C"],["G","G","C","G"],["A","T","G","T"],["G","C","A","C"],["A","G","T","G"],["C","A","A","A"],["G","T","G","T"],["T","A","A","A"],["C","G","C","G"],["G","A","G","A"],["A","C","T","C"],["G","G","C","C"],["A","T","G","G"],["C","C","A","T"],["T","G","T","A"],["G","A","A","G"],["A","T","G","A"],["T","A","A","C"],["A","G","G","G"],["G","A","A","T"],["A","G","T","C"],["C","T","A","G"],["G","A","G","A"],["T","T","A","T"],["C","A","C","A"],["G","G","G","G"],["A","A","T","A"],["T","C","C","C"],["A","C","G","G"],["G","G","A","T"],["A","T","T","C"],["C","G","A","G"],["G","A","G","A"],["T","T","A","T"],["C","A","C","A"],["G","G","C","G"],["A","A","G","A"],["T","C","T","C"],["A","G","A","C"],["G","T","T","G"],["A","C","A","T"],["C","G","G"],["C","A","A"],["G","G","A"],["A","A","G"],["T","A","A"],["A","G","C"],["G","A","G"],["A","C","T"],["C","G","C"],["T","T","G"],["C","C","A"],["G","G","T"],["T","A","A"],["G","T","G"],["A","A","A"],["T","G","T"],["A","A","A"],["G","C","G"]]},"header":{"fill":{},"line":{"color":"white","width":0.0},"values":["","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|","","","","","","","","","","|"]}}];""" - |> chartGeneratedContains sequencePresentationTableChart - ); - testCase "Sequence presentation table layout" ( fun () -> - "var layout = {\"width\":650,\"title\":{\"text\":\"Sequence A\"}};" - |> chartGeneratedContains sequencePresentationTableChart - ); - ] - - -let heatmap1Chart = - let matrix = - [[1.;1.5;0.7;2.7]; - [2.;0.5;1.2;1.4]; - [0.1;2.6;2.4;3.0];] - - let rownames = ["p3";"p2";"p1"] - let colnames = ["Tp0";"Tp30";"Tp60";"Tp160"] - - let colorscaleValue = - StyleParam.Colorscale.Custom [(0.0,Color.fromString "#3D9970");(1.0,Color.fromString "#001f3f")] - - Chart.Heatmap( - zData = matrix, - colNames = colnames, - rowNames = rownames, - ColorScale=colorscaleValue, - ShowScale=true, - UseDefaults = false - ) - |> Chart.withSize(700,500) - |> Chart.withMarginSize(Left=200.) - -let heatmapStyledChart = - let matrix = - [[1.;1.5;0.7;2.7]; - [2.;0.5;1.2;1.4]; - [0.1;2.6;2.4;3.0];] - - let rownames = ["p3";"p2";"p1"] - let colnames = ["Tp0";"Tp30";"Tp60";"Tp160"] - - let colorscaleValue = - StyleParam.Colorscale.Custom [(0.0,Color.fromString "#3D9970");(1.0,Color.fromString "#001f3f")] - - Chart.Heatmap( - zData = matrix, - colNames = colnames, - rowNames = rownames, - ColorScale=colorscaleValue, - ShowScale=true, - UseDefaults = false - ) - |> Chart.withSize(700.,500.) - |> Chart.withMarginSize(Left=200.) - |> Chart.withColorBarStyle(TitleText = "Im the Colorbar") - -let annotatedheatmap = - Chart.AnnotatedHeatmap( - zData = [ - [1..5] - [6..10] - [11..15] - ], - annotationText = [ - ["1,1";"1,2";"1,3"] - ["2,1";"2,2";"2,3"] - ["3,1";"3,2";"3,3"] - ], - X = ["C1";"C2";"C3"], - Y = ["R1";"R2";"R3"], - ReverseYAxis = true, - UseDefaults = false - ) - -[] -let ``Heatmap charts`` = - testList "SimpleCharts.Heatmap charts" [ - testCase "Heatmap data" ( fun () -> - """var data = [{"type":"heatmap","x":["Tp0","Tp30","Tp60","Tp160"],"y":["p3","p2","p1"],"z":[[1.0,1.5,0.7,2.7],[2.0,0.5,1.2,1.4],[0.1,2.6,2.4,3.0]],"colorscale":[[0.0,"#3D9970"],[1.0,"#001f3f"]],"showscale":true}];""" - |> chartGeneratedContains heatmap1Chart - ); - testCase "Heatmap layout" ( fun () -> - "var layout = {\"width\":700,\"height\":500,\"margin\":{\"l\":200.0}};" - |> chartGeneratedContains heatmap1Chart - ); - testCase "Heatmap styled data" ( fun () -> - """var data = [{"type":"heatmap","x":["Tp0","Tp30","Tp60","Tp160"],"y":["p3","p2","p1"],"z":[[1.0,1.5,0.7,2.7],[2.0,0.5,1.2,1.4],[0.1,2.6,2.4,3.0]],"colorscale":[[0.0,"#3D9970"],[1.0,"#001f3f"]],"showscale":true,"colorbar":{"title":{"text":"Im the Colorbar"}}}];""" - |> chartGeneratedContains heatmapStyledChart - ); - testCase "Heatmap styled layout" ( fun () -> - "var layout = {\"width\":700,\"height\":500,\"margin\":{\"l\":200.0}};" - |> chartGeneratedContains heatmapStyledChart - ); - testCase "Annotated heatmap data" ( fun () -> - """var data = [{"type":"heatmap","x":["C1","C2","C3"],"y":["R1","R2","R3"],"z":[[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]}];""" - |> chartGeneratedContains annotatedheatmap - ); - testCase "Annotated heatmap layout" ( fun () -> - """var layout = {"yaxis":{"autorange":"reversed"},"annotations":[{"x":0,"y":0,"showarrow":false,"text":"1,1"},{"x":1,"y":0,"showarrow":false,"text":"1,2"},{"x":2,"y":0,"showarrow":false,"text":"1,3"},{"x":0,"y":1,"showarrow":false,"text":"2,1"},{"x":1,"y":1,"showarrow":false,"text":"2,2"},{"x":2,"y":1,"showarrow":false,"text":"2,3"},{"x":0,"y":2,"showarrow":false,"text":"3,1"},{"x":1,"y":2,"showarrow":false,"text":"3,2"},{"x":2,"y":2,"showarrow":false,"text":"3,3"}]};""" - |> chartGeneratedContains annotatedheatmap - ); - ] - - -let colors = [ - [[0 ;0 ;255]; [255;255;0 ]; [0 ;0 ;255]] - [[255;0 ;0 ]; [255;0 ;255]; [255;0 ;255]] - [[0 ;255;0 ]; [0 ;255;255]; [255;0 ;0 ]] -] - -let imageRawChart = - Chart.Image(Z=colors, UseDefaults = false) - |> Chart.withTitle "Image chart from raw color component arrays" - -let imageRawHSLChart = - Chart.Image(Z=colors, ColorModel=StyleParam.ColorModel.HSL, UseDefaults = false) - |> Chart.withTitle "HSL color model" - -let argbs = [ - [ColorKeyword.AliceBlue ; ColorKeyword.CornSilk ; ColorKeyword.LavenderBlush ] |> List.map ARGB.fromKeyword - [ColorKeyword.DarkGray ; ColorKeyword.Snow ; ColorKeyword.MidnightBlue ] |> List.map ARGB.fromKeyword - [ColorKeyword.LightSteelBlue; ColorKeyword.DarkKhaki; ColorKeyword.LightAkyBlue ] |> List.map ARGB.fromKeyword -] - -let imageARGBChart = - Chart.Image(z = argbs, UseDefaults = false) - |> Chart.withTitle "ARGB image chart" - -open System.IO - -let imageSource = $@"{__SOURCE_DIRECTORY__}../../../../docs/img/logo.png" - -let base64String = - imageSource - |> File.ReadAllBytes - |> System.Convert.ToBase64String - -let logoImageChart = - Chart.Image( - Source=($"data:image/jpg;base64,{base64String}"), - UseDefaults = false - ) - |> Chart.withTitle "This is Plotly.NET:" - -[] -let ``Image charts`` = - testList "SimpleCharts.Image charts" [ - testCase "Image raw data" ( fun () -> - """var data = [{"type":"image","z":[[[0,0,255],[255,255,0],[0,0,255]],[[255,0,0],[255,0,255],[255,0,255]],[[0,255,0],[0,255,255],[255,0,0]]]}];""" - |> chartGeneratedContains imageRawChart - ); - testCase "Image raw layout" ( fun () -> - """var layout = {"title":{"text":"Image chart from raw color component arrays"}};""" - |> chartGeneratedContains imageRawChart - ); - - testCase "Image raw hsl data" ( fun () -> - """var data = [{"type":"image","z":[[[0,0,255],[255,255,0],[0,0,255]],[[255,0,0],[255,0,255],[255,0,255]],[[0,255,0],[0,255,255],[255,0,0]]],"colormodel":"hsl"}];""" - |> chartGeneratedContains imageRawHSLChart - ); - testCase "Image raw hsl layout" ( fun () -> - """var layout = {"title":{"text":"HSL color model"}};""" - |> chartGeneratedContains imageRawHSLChart - ); - - testCase "Image ARGB data" ( fun () -> - """var data = [{"type":"image","z":[[[240,248,255,255],[255,248,220,255],[255,240,245,255]],[[169,169,169,255],[255,250,250,255],[25,25,112,255]],[[176,196,222,255],[189,183,107,255],[135,206,250,255]]],"colormodel":"rgba"}];""" - |> chartGeneratedContains imageARGBChart - ); - testCase "Image ARGB layout" ( fun () -> - """var layout = {"title":{"text":"ARGB image chart"}};""" - |> chartGeneratedContains imageARGBChart - ); - - testCase "Image base64 data" ( fun () -> - """var data = [{"type":"image","source":"data:image/jpg;base64,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RAFgOgIKUERA4XBCClBCSAFgSr+4egAA7ElAAYoIKIF//Zf/+rurN4zmL7/76g/btn139Q5geb/etu2nb374+durhwDAnkQUAKYhoABFBJTAj19//5v//ad//I9vfvj5N1dvGY2QApQQUgCYjogCwBQEFKCIgBL48evvf7Nt25//75e//Idt2/4spNxPSAFKCCkATEVEAWB4AgpQREAJPAeUbdt+9XT61SakRIQUoISQAsA0RBQAhiagAEUElMALAeWZkBISUoASQgoAUxBRABiWgAIUEVACbwSUZ0JKSEgBSggpAAxPRAFgSAIKUERACdwQUJ4JKSEhBSghpAAwNBEFgOEIKEARASVwR0B5JqSEhBSghJACwLBEFACGIqAARQSUQBBQngkpISEFKCGkADAkEQWAYQgoQBEBJfBAQHkmpISEFKCEkALAcEQUAIYgoABFBJTADgHlmZASElKAEkIKAEMRUQCoJ6AARQSUwI4B5ZmQEhJSgBJCCgDDEFEAqCagAEUElMABAeWZkBISUoASQgoAQxBRAKgloABFBJTAgQHlmZASElKAEkIKAPVEFAAqCShAEQElcEJAeSakhIQUoISQAkA1EQWAOgIKUERACZwYUJ4JKSEhBSghpABQS0QBoIqAAhQRUAIXBJRnQkpISAFKCCkAVBJRAKghoABFBJTAhQHlmZASElKAEkIKAHVEFAAqCChAEQElUBBQngkpISEFKCGkAFBFRAHgcgIKUERACRQFlGdCSkhIAUoIKQDUEFEAuJSAAhQRUAKFAeWZkBISUoASQgoAFUQUAC4joABFBJRAcUB5JqSEhBSghJACwOVEFAAuIaAARQSUwAAB5ZmQEhJSgBJCCgCXElEAOJ2AAhQRUAIDBZRnQkpISAFKCCkAXEZEAeBUAgpQREAJDBhQngkpISEFKCGkAHAJEQWA0wgoQBEBJTBwQHkmpISEFKCEkALA6UQUAE4hoABFBJTABAHlmZASElKAEkIKAKcSUQA4nIACFBFQAhMFlGdCSkhIAUoIKQCcRkQB4FACClBEQAlMGFCeCSkhIQUoIaQAcAoRBYDDCChAEQElMHFAeSakhIQUoISQAsDhRBQADiGgAEUElMACAeWZkBISUoASQgoAhxJRANidgAIUEVACCwWUZ0JKSEgBSggpABxGRAFgVwIKUERACSwYUJ4JKSEh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"}];""" - |> chartGeneratedContains logoImageChart - ); - testCase "Image base64 layout" ( fun () -> - """var layout = {"title":{"text":"This is Plotly.NET:"}};""" - |> chartGeneratedContains logoImageChart - ); - - - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/SmithCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/SmithCharts.fs deleted file mode 100644 index 790c23dac..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/SmithCharts.fs +++ /dev/null @@ -1,95 +0,0 @@ -module Tests.SmithCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open System - -open TestUtils.HtmlCodegen - -let scatterSmithChart = - Chart.ScatterSmith( - real = [0.5; 1.; 2.; 3.], - imag = [0.5; 1.; 2.; 3.], - mode = StyleParam.Mode.Lines_Markers_Text, - MultiText = ["Pretty"; "Cool"; "Plot"; "Huh?"], - TextPosition = StyleParam.TextPosition.TopCenter, - UseDefaults = false - ) - -let pointSmithChart = - Chart.PointSmith( - real = [0.5; 1.; 2.; 3.], - imag = [0.5; 1.; 2.; 3.], - UseDefaults = false - ) - -let lineSmithChart = - Chart.LineSmith( - real = [0.5; 1.; 2.; 3.], - imag = [0.5; 1.; 2.; 3.], - LineDash = StyleParam.DrawingStyle.DashDot, - LineColor = Color.fromKeyword Purple, - UseDefaults = false - ) - -let bubbleSmithChart = - Chart.BubbleSmith( - real = [0.5; 1.; 2.; 3.], - imag = [0.5; 1.; 2.; 3.], - sizes = [10;20;30;40], - MultiText=["one";"two";"three";"four";"five";"six";"seven"], - TextPosition=StyleParam.TextPosition.TopCenter, - UseDefaults = false - ) - - -[] -let ``Smith Scatter charts`` = - testList "SmithCharts.Scatter charts" [ - testCase "Scatter data" ( fun () -> - """var data = [{"type":"scattersmith","mode":"lines+markers+text","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"text":["Pretty","Cool","Plot","Huh?"],"textposition":"top center","marker":{},"line":{}}];""" - |> chartGeneratedContains scatterSmithChart - ) - testCase "Scatter layout" ( fun () -> - emptyLayout scatterSmithChart - ) - ] - -[] -let ``Smith Point charts`` = - testList "SmithCharts.Point charts" [ - testCase "Point data" ( fun () -> - """var data = [{"type":"scattersmith","mode":"markers","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"marker":{},"line":{}}];""" - |> chartGeneratedContains pointSmithChart - ) - testCase "Point layout" ( fun () -> - emptyLayout pointSmithChart - ) - ] - -[] -let ``Smith Line charts`` = - testList "SmithCharts.Line charts" [ - testCase "Line data" ( fun () -> - """var data = [{"type":"scattersmith","mode":"lines","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"marker":{},"line":{"color":"rgba(128, 0, 128, 1.0)","dash":"dashdot"}}];""" - |> chartGeneratedContains lineSmithChart - ) - testCase "Line layout" ( fun () -> - emptyLayout lineSmithChart - ) - ] - -[] -let ``Smith Bubble charts`` = - testList "SmithCharts.Bubble charts" [ - testCase "Bubble data" ( fun () -> - """var data = [{"type":"scattersmith","mode":"markers+text","imag":[0.5,1.0,2.0,3.0],"real":[0.5,1.0,2.0,3.0],"text":["one","two","three","four","five","six","seven"],"textposition":"top center","marker":{"size":[10,20,30,40]},"line":{}}];""" - |> chartGeneratedContains bubbleSmithChart - ) - testCase "Bubble layout" ( fun () -> - emptyLayout bubbleSmithChart - ) - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/HtmlCodegen/TernaryCharts.fs b/tests/Plotly.NET.Tests/HtmlCodegen/TernaryCharts.fs deleted file mode 100644 index 5422ac0ee..000000000 --- a/tests/Plotly.NET.Tests/HtmlCodegen/TernaryCharts.fs +++ /dev/null @@ -1,59 +0,0 @@ -module Tests.TernaryCharts - -open Expecto -open Plotly.NET -open Plotly.NET.LayoutObjects -open Plotly.NET.TraceObjects -open Plotly.NET.GenericChart -open System - -open TestUtils.HtmlCodegen - -let pointTernary = - Chart.PointTernary(abc = [1,2,3], UseDefaults = false) - |> Chart.withAAxis(LinearAxis.init(Title = Title.init("A"), Color = Color.fromKeyword ColorKeyword.DarkOrchid)) - |> Chart.withBAxis(LinearAxis.init(Title = Title.init("B"), Color = Color.fromKeyword ColorKeyword.DarkRed)) - |> Chart.withCAxis(LinearAxis.init(Title = Title.init("C"), Color = Color.fromKeyword ColorKeyword.DarkCyan)) - -let lineTernary = - Chart.LineTernary( - A = [10; 20; 30; 40; 50; 60; 70; 80;], - B = ([10; 20; 30; 40; 50; 60; 70; 80;] |> List.rev), - Sum = 100, - ShowMarkers = true, - LineDash = StyleParam.DrawingStyle.DashDot, - UseDefaults = false - ) - |> Chart.withTernary( - Ternary.init( - AAxis = LinearAxis.init(Color = Color.fromKeyword ColorKeyword.DarkOrchid), - BAxis = LinearAxis.init(Color = Color.fromKeyword ColorKeyword.DarkRed), - CAxis = LinearAxis.init(Color = Color.fromKeyword ColorKeyword.DarkCyan) - ) - ) - -[] -let ``Ternary Point charts`` = - testList "TernaryCharts.Point charts" [ - testCase "Point data" ( fun () -> - """var data = [{"type":"scatterternary","mode":"markers","a":[1],"b":[2],"c":[3],"marker":{},"line":{}}];""" - |> chartGeneratedContains pointTernary - ) - testCase "Point layout" ( fun () -> - """var layout = {"ternary":{"aaxis":{"color":"rgba(153, 50, 204, 1.0)","title":{"text":"A"}},"baxis":{"color":"rgba(139, 0, 0, 1.0)","title":{"text":"B"}},"caxis":{"color":"rgba(0, 139, 139, 1.0)","title":{"text":"C"}}}};""" - |> chartGeneratedContains pointTernary - ) - ] - -[] -let ``Ternary Line charts`` = - testList "TernaryCharts.Line charts" [ - testCase "Line data" ( fun () -> - """var data = [{"type":"scatterternary","mode":"lines+markers","a":[10,20,30,40,50,60,70,80],"b":[80,70,60,50,40,30,20,10],"marker":{},"line":{"dash":"dashdot"},"sum":100}];""" - |> chartGeneratedContains lineTernary - ) - testCase "Line layout" ( fun () -> - """var layout = {"ternary":{"aaxis":{"color":"rgba(153, 50, 204, 1.0)"},"baxis":{"color":"rgba(139, 0, 0, 1.0)"},"caxis":{"color":"rgba(0, 139, 139, 1.0)"}}};""" - |> chartGeneratedContains lineTernary - ) - ] \ No newline at end of file diff --git a/tests/Plotly.NET.Tests/TestUtils.fs b/tests/Plotly.NET.Tests/TestUtils.fs deleted file mode 100644 index f24e7f1af..000000000 --- a/tests/Plotly.NET.Tests/TestUtils.fs +++ /dev/null @@ -1,48 +0,0 @@ -module TestUtils - -open Expecto -open DynamicObj -open Newtonsoft.Json -open Plotly.NET.GenericChart -open System.Reflection -open System.IO - -module HtmlCodegen = - - let getFullPlotlyJS() = - let assembly = Assembly.GetExecutingAssembly() - use str = assembly.GetManifestResourceStream($"Plotly.NET.Tests.plotly-{Globals.PLOTLYJS_VERSION}.min.js") - use r = new StreamReader(str) - r.ReadToEnd() - - let substringIsInChart chart htmlizer substring = - chart - |> htmlizer - |> Expect.stringContains - |> (fun expecting -> expecting substring $"Should've contained {substring}") - - - let substringListIsInChart chart htmlizer substringList = - for substring in substringList do - substringIsInChart chart htmlizer substring - - - let chartGeneratedContains chart substring = - substringIsInChart chart toChartHTML substring - substringIsInChart chart toEmbeddedHTML substring - - - let chartGeneratedContainsList chart substringList = - for substring in substringList do - chartGeneratedContains chart substring - - let emptyLayout chart = - "var layout = {};" |> chartGeneratedContains chart - -module Objects = - - let jsonFieldIsSetWith fieldName expected (object:#DynamicObj) = - Expect.equal - ((object :> DynamicObj)?($"{fieldName}") |> JsonConvert.SerializeObject) - expected - ($"Field `{fieldName}` not set correctly in serialized dynamic object.") diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..07a99f0b2 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,83 @@ +# The Plotly.NET Test Suite + +This folder contains all tests performed in this monorepo. + +As It got quite large and complex, this reamde is a try to make things less opaque for contributors. + +## Testing frameworks used + +- Most of the F# test projects use [Expecto](https://github.com/haf/expecto), some use [xUnit](https://xunit.net/) + +- The C# test projects use [xUnit](https://xunit.net/) + +- The JS test projects use [Mocha](https://mochajs.org/) + +## Running tests + +### Using the `build` project + +This is the main way of running tests from the command line. + +Either use `build.cmd` or `build.sh` depending on your OS to run test targets, e.g. : + +``` +build.cmd runTestsAll +build.sh runTestsCore +``` + +Available test targets are: + +- `RunTestsAll` - runs all tests +- `RunTestsAllNoNetFX` - runs all tests except the ones that require .NET Framework +- `RunTestsCore` - runs all tests in the `CoreTests` folder except the ones that require .NET Framework +- `RunTestsCoreWithNetFX` - runs all tests in the `CoreTests` folder including the ones that require .NET Framework +- `RunTestsNetFX` - runs all tests that require .NET Framework +- `RunTestsExtensionLibs` - runs all tests in the `ExtensionLibTests` folder +- `RunTestsJSTests` - runs all tests in the `JSTests` folder + +### Using Visual Studio Test Explorer + +You can also run tests from within Visual Studio using the Test Explorer. + +Just note that test projects targeting .NET Framework will not be visible in the Test Explorer, as they are not supported by the Test Explorer. + +## Folder structure + +### Common + +`Common` contains all common code used in the test suite. +This includes the `TestUtils` and `TestCharts` modules, which contain utility functions for testing and the actual chart objets to use in tests. + +The projects contained here are `FSharpTestBase.fsproj` and `CSharptestBase.csproj` - classlibs that contain the above for each language respectively. + +### ConsoleApps + +`ConsoleApps` is a folder containing projects that use any amount of the other projects in this repo. +They are not TestProjects as in that they contain UnitTests, but are more thought of as playgrounds +to manually test functionality of the projects in a console application. + +### CoreTests + +`CoreTests` contains all UnitTests for the core `Plotly.NET` F# project. This is by far the largest test suite. + +Currently, the following test projects are contained: + +#### CoreTests.fsproj + +The largets test suite by far that tests all things Plotly.NET, such as HTML code generation, JSON serialization, Object validity, etc. + +#### CSharpInteroperabilityTests.csproj + +Testing usage of the core F# API from C# + +#### StrongNameTests.fsproj + +Testing usage of the core F# in an environment that requires strong named libraries. This catches for example the addition of new dependencies that are not signed, and therefore cause errors in such an environment, but not in newer .NET environments. + +### ExtensionLibTests + +`ExtensionLibTests` contains all UnitTests for extension libraries, such as `Plotly.NET.CSharp` or `Plotly.NET.ImageExport`. + +### JSTests + +`JSTests` contains all UnitTests that run on node.js. The main application here is the validation of generated JSON via the `Plotly.validate` function in the `plotly.js` library. \ No newline at end of file