Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 258 additions & 0 deletions tests/FSharp.Data.Core.Tests/BaseTypesHtmlDocument.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
module FSharp.Data.Tests.BaseTypesHtmlDocument

open System.IO
open NUnit.Framework
open FsUnit
open FSharp.Data
open FSharp.Data.Runtime.BaseTypes

#nowarn "10001" // Suppress "intended for use in generated code only" warnings

[<Test>]
let ``HtmlDocument.Create successfully parses HTML with tables`` () =
let htmlContent = """
<html>
<body>
<table id="test-table">
<tr><th>Name</th><th>Age</th></tr>
<tr><td>John</td><td>30</td></tr>
</table>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

htmlDoc.Html.ToString() |> should contain "test-table"

[<Test>]
let ``HtmlDocument.Create with includeLayoutTables true`` () =
let htmlContent = """
<html>
<body>
<table id="layout-table">
<tr><td>Layout cell</td></tr>
</table>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(true, reader)

htmlDoc.Html.ToString() |> should contain "layout-table"

[<Test>]
let ``HtmlDocument.Create with includeLayoutTables false`` () =
let htmlContent = """
<html>
<body>
<table id="data-table">
<tr><td>Data cell</td></tr>
</table>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

htmlDoc.Html.ToString() |> should contain "data-table"

[<Test>]
let ``HtmlDocument.Html property returns the parsed document`` () =
let htmlContent = """<html><body><h1>Test Title</h1></body></html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)
let doc = htmlDoc.Html

doc.ToString() |> should contain "Test Title"

[<Test>]
let ``HtmlDocument.GetTable retrieves table by id`` () =
let htmlContent = """
<html>
<body>
<table id="data-table">
<tr><th>Column1</th><th>Column2</th></tr>
<tr><td>Value1</td><td>Value2</td></tr>
</table>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)
let table = htmlDoc.GetTable("data-table")

table.Name |> should equal "data-table"

[<Test>]
let ``HtmlDocument.GetTable throws when table not found`` () =
let htmlContent = """<html><body><p>No tables here</p></body></html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

(fun () -> htmlDoc.GetTable("nonexistent") |> ignore) |> should throw typeof<System.Collections.Generic.KeyNotFoundException>

[<Test>]
let ``HtmlDocument.GetList retrieves list by id`` () =
let htmlContent = """
<html>
<body>
<ul id="item-list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)
let list = htmlDoc.GetList("item-list")

list.Name |> should equal "item-list"

[<Test>]
let ``HtmlDocument.GetList works with ordered lists`` () =
let htmlContent = """
<html>
<body>
<ol id="numbered-list">
<li>First</li>
<li>Second</li>
</ol>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)
let list = htmlDoc.GetList("numbered-list")

list.Name |> should equal "numbered-list"

[<Test>]
let ``HtmlDocument.GetList throws when list not found`` () =
let htmlContent = """<html><body><p>No lists here</p></body></html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

(fun () -> htmlDoc.GetList("nonexistent") |> ignore) |> should throw typeof<System.Collections.Generic.KeyNotFoundException>

[<Test>]
let ``HtmlDocument.GetDefinitionList retrieves definition list by id`` () =
let htmlContent = """
<html>
<body>
<dl id="def-list">
<dt>Term1</dt>
<dd>Definition1</dd>
<dt>Term2</dt>
<dd>Definition2</dd>
</dl>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)
let defList = htmlDoc.GetDefinitionList("def-list")

defList.Name |> should equal "def-list"

[<Test>]
let ``HtmlDocument.GetDefinitionList throws when definition list not found`` () =
let htmlContent = """<html><body><p>No definition lists here</p></body></html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

(fun () -> htmlDoc.GetDefinitionList("nonexistent") |> ignore) |> should throw typeof<System.Collections.Generic.KeyNotFoundException>

[<Test>]
let ``HtmlDocument.Create handles empty HTML`` () =
let htmlContent = "<html><body></body></html>"

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

htmlDoc.Html.ToString() |> should not' (equal "")

[<Test>]
let ``HtmlDocument.Create handles malformed HTML gracefully`` () =
let htmlContent = "<html><body><h1>Test Content</h1><p>Valid paragraph</p><div>Unclosed div"

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

// Parser handles malformed HTML by auto-closing tags and preserving content
let htmlString = htmlDoc.Html.ToString()
htmlString |> should contain "Test Content"
htmlString |> should contain "Valid paragraph"
// The parser should preserve at least some structure even with malformed HTML

[<Test>]
let ``HtmlDocument.Create processes multiple tables correctly`` () =
let htmlContent = """
<html>
<body>
<table id="table1">
<tr><td>Table 1 Content</td></tr>
</table>
<table id="table2">
<tr><td>Table 2 Content</td></tr>
</table>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

// Use the actual generated names since HTML parsing creates unique names
htmlDoc.Html.ToString() |> should contain "Table 1 Content"
htmlDoc.Html.ToString() |> should contain "Table 2 Content"

[<Test>]
let ``HtmlDocument.Create processes multiple lists correctly`` () =
let htmlContent = """
<html>
<body>
<ul id="list1">
<li>List 1 Item</li>
</ul>
<ol id="list2">
<li>List 2 Item</li>
</ol>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

// Verify the content is parsed correctly
htmlDoc.Html.ToString() |> should contain "List 1 Item"
htmlDoc.Html.ToString() |> should contain "List 2 Item"

[<Test>]
let ``HtmlDocument.Create processes multiple definition lists correctly`` () =
let htmlContent = """
<html>
<body>
<dl id="def1">
<dt>Term A</dt>
<dd>Definition A</dd>
</dl>
<dl id="def2">
<dt>Term B</dt>
<dd>Definition B</dd>
</dl>
</body>
</html>"""

use reader = new StringReader(htmlContent)
let htmlDoc = HtmlDocument.Create(false, reader)

// Verify the definition lists are parsed correctly
htmlDoc.Html.ToString() |> should contain "Term A"
htmlDoc.Html.ToString() |> should contain "Definition A"
htmlDoc.Html.ToString() |> should contain "Term B"
htmlDoc.Html.ToString() |> should contain "Definition B"
1 change: 1 addition & 0 deletions tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<Compile Include="HtmlOperations.fs" />
<Compile Include="HtmlAttributeExtensions.fs" />
<Compile Include="HtmlCssSelectors.fs" />
<Compile Include="BaseTypesHtmlDocument.fs" />
<Compile Include="XmlExtensions.fs" />
<Compile Include="WorldBankRuntime.fs" />
<Compile Include="Program.fs" />
Expand Down
94 changes: 94 additions & 0 deletions tests/FSharp.Data.Core.Tests/JsonConversions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,97 @@ let ``Conversions handle different number formats`` () =
JsonValue.String "123.456" |> asDecimal |> should equal (Some 123.456M)
JsonValue.String "0" |> asDecimal |> should equal (Some 0M)
JsonValue.String "-99.99" |> asDecimal |> should equal (Some -99.99M)

[<Test>]
let ``Integer conversions test range boundaries for helper functions`` () =
let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture
let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture

// Test exact boundary values to trigger inRangeDecimal helper function
JsonValue.Number (decimal System.Int32.MaxValue) |> asInteger |> should equal (Some System.Int32.MaxValue)
JsonValue.Number (decimal System.Int32.MinValue) |> asInteger |> should equal (Some System.Int32.MinValue)

// Test values just outside boundaries to trigger inRangeDecimal helper function
JsonValue.Number ((decimal System.Int32.MaxValue) + 1M) |> asInteger |> should equal None
JsonValue.Number ((decimal System.Int32.MinValue) - 1M) |> asInteger |> should equal None

// Test exact boundary values for Int64 to trigger inRangeDecimal helper function
JsonValue.Number (decimal System.Int64.MaxValue) |> asInteger64 |> should equal (Some System.Int64.MaxValue)
JsonValue.Number (decimal System.Int64.MinValue) |> asInteger64 |> should equal (Some System.Int64.MinValue)

[<Test>]
let ``Float integer conversions test range boundaries for helper functions`` () =
let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture
let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture

// Test exact boundary values with floats to trigger inRangeFloat helper function
JsonValue.Float (float System.Int32.MaxValue) |> asInteger |> should equal (Some System.Int32.MaxValue)
JsonValue.Float (float System.Int32.MinValue) |> asInteger |> should equal (Some System.Int32.MinValue)

// Test values just outside boundaries with floats to trigger inRangeFloat helper function
JsonValue.Float ((float System.Int32.MaxValue) + 1.0) |> asInteger |> should equal None
JsonValue.Float ((float System.Int32.MinValue) - 1.0) |> asInteger |> should equal None

// Test large values for Int64 with floats to trigger inRangeFloat helper function
// Use values that don't hit floating-point precision limits
JsonValue.Float 1000000000000.0 |> asInteger64 |> should equal (Some 1000000000000L)
JsonValue.Float -1000000000000.0 |> asInteger64 |> should equal (Some -1000000000000L)

[<Test>]
let ``Integer detection tests for decimal values to trigger isIntegerDecimal helper`` () =
let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture
let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture

// Test decimal values that are integers to trigger isIntegerDecimal helper function
JsonValue.Number 42.0M |> asInteger |> should equal (Some 42)
JsonValue.Number -123.000M |> asInteger |> should equal (Some -123)
JsonValue.Number 0.0M |> asInteger |> should equal (Some 0)
JsonValue.Number 1.0M |> asInteger64 |> should equal (Some 1L)

// Test decimal values that are NOT integers to trigger isIntegerDecimal helper function
JsonValue.Number 42.1M |> asInteger |> should equal None
JsonValue.Number -123.5M |> asInteger |> should equal None
JsonValue.Number 0.001M |> asInteger |> should equal None
JsonValue.Number 1.99999M |> asInteger64 |> should equal None

[<Test>]
let ``Integer detection tests for float values to trigger isIntegerFloat helper`` () =
let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture
let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture

// Test float values that are integers to trigger isIntegerFloat helper function
JsonValue.Float 42.0 |> asInteger |> should equal (Some 42)
JsonValue.Float -123.000 |> asInteger |> should equal (Some -123)
JsonValue.Float 0.0 |> asInteger |> should equal (Some 0)
JsonValue.Float 1.0 |> asInteger64 |> should equal (Some 1L)

// Test float values that are NOT integers to trigger isIntegerFloat helper function
JsonValue.Float 42.1 |> asInteger |> should equal None
JsonValue.Float -123.5 |> asInteger |> should equal None
JsonValue.Float 0.001 |> asInteger |> should equal None
JsonValue.Float 1.99999 |> asInteger64 |> should equal None

// Test special float cases to trigger isIntegerFloat helper function
JsonValue.Float System.Double.NaN |> asInteger |> should equal None
JsonValue.Float System.Double.PositiveInfinity |> asInteger |> should equal None
JsonValue.Float System.Double.NegativeInfinity |> asInteger64 |> should equal None

[<Test>]
let ``Combined edge cases to exercise all helper functions`` () =
let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture
let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture

// Large integers that exercise both range checking and integer detection helpers
JsonValue.Number 2147483647.0M |> asInteger |> should equal (Some 2147483647) // Int32.MaxValue
JsonValue.Float 2147483647.0 |> asInteger |> should equal (Some 2147483647)

JsonValue.Number 2147483648.0M |> asInteger |> should equal None // Just over Int32.MaxValue
JsonValue.Float 2147483648.0 |> asInteger |> should equal None

// Large values that work for Int64 but not Int32
JsonValue.Number 3000000000M |> asInteger |> should equal None
JsonValue.Number 3000000000M |> asInteger64 |> should equal (Some 3000000000L)

// Values that are in range but not integers
JsonValue.Number 100.5M |> asInteger |> should equal None
JsonValue.Float 100.5 |> asInteger |> should equal None
Loading