Merge pull request #14682 from dotnet-maestro-bot/merge/release/3.1-preview1-to-release/3.1
[automated] Merge branch 'release/3.1-preview1' => 'release/3.1'
This commit is contained in:
commit
79f04eb14d
|
|
@ -231,7 +231,7 @@
|
||||||
<CastleCorePackageVersion>4.2.1</CastleCorePackageVersion>
|
<CastleCorePackageVersion>4.2.1</CastleCorePackageVersion>
|
||||||
<FSharpCorePackageVersion>4.2.1</FSharpCorePackageVersion>
|
<FSharpCorePackageVersion>4.2.1</FSharpCorePackageVersion>
|
||||||
<GoogleProtobufPackageVersion>3.8.0</GoogleProtobufPackageVersion>
|
<GoogleProtobufPackageVersion>3.8.0</GoogleProtobufPackageVersion>
|
||||||
<GrpcAspNetCorePackageVersion>2.23.1</GrpcAspNetCorePackageVersion>
|
<GrpcAspNetCorePackageVersion>2.23.2</GrpcAspNetCorePackageVersion>
|
||||||
<IdentityServer4AspNetIdentityPackageVersion>3.0.0</IdentityServer4AspNetIdentityPackageVersion>
|
<IdentityServer4AspNetIdentityPackageVersion>3.0.0</IdentityServer4AspNetIdentityPackageVersion>
|
||||||
<IdentityServer4EntityFrameworkPackageVersion>3.0.0</IdentityServer4EntityFrameworkPackageVersion>
|
<IdentityServer4EntityFrameworkPackageVersion>3.0.0</IdentityServer4EntityFrameworkPackageVersion>
|
||||||
<IdentityServer4PackageVersion>3.0.0</IdentityServer4PackageVersion>
|
<IdentityServer4PackageVersion>3.0.0</IdentityServer4PackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@page "/"
|
@page "/"
|
||||||
|
|
||||||
<h1>Hello, world!</h1>
|
<h1>Hello, world!</h1>
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -341,9 +341,18 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryApplyValueProperty(batch: RenderBatch, element: Element, attributeFrame: RenderTreeFrame | null) {
|
private tryApplyValueProperty(batch: RenderBatch, element: Element, attributeFrame: RenderTreeFrame | null): boolean {
|
||||||
// Certain elements have built-in behaviour for their 'value' property
|
// Certain elements have built-in behaviour for their 'value' property
|
||||||
const frameReader = batch.frameReader;
|
const frameReader = batch.frameReader;
|
||||||
|
|
||||||
|
if (element.tagName === 'INPUT' && element.getAttribute('type') === 'time' && !element.getAttribute('step')) {
|
||||||
|
const timeValue = attributeFrame ? frameReader.attributeValue(attributeFrame) : null;
|
||||||
|
if (timeValue) {
|
||||||
|
element['value'] = timeValue.substring(0, 5);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (element.tagName) {
|
switch (element.tagName) {
|
||||||
case 'INPUT':
|
case 'INPUT':
|
||||||
case 'SELECT':
|
case 'SELECT':
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
export class EventForDotNet<TData extends UIEventArgs> {
|
export class EventForDotNet<TData extends UIEventArgs> {
|
||||||
constructor(public readonly type: EventArgsType, public readonly data: TData) {
|
public constructor(public readonly type: EventArgsType, public readonly data: TData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromDOMEvent(event: Event): EventForDotNet<UIEventArgs> {
|
public static fromDOMEvent(event: Event): EventForDotNet<UIEventArgs> {
|
||||||
const element = event.target as Element;
|
const element = event.target as Element;
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
|
|
||||||
case 'input':
|
case 'input':
|
||||||
case 'change': {
|
case 'change': {
|
||||||
|
|
||||||
|
if (isTimeBasedInput(element)) {
|
||||||
|
const normalizedValue = normalizeTimeBasedValue(element);
|
||||||
|
return new EventForDotNet<UIChangeEventArgs>('change', { type: event.type, value: normalizedValue });
|
||||||
|
}
|
||||||
|
|
||||||
const targetIsCheckbox = isCheckbox(element);
|
const targetIsCheckbox = isCheckbox(element);
|
||||||
const newValue = targetIsCheckbox ? !!element['checked'] : element['value'];
|
const newValue = targetIsCheckbox ? !!element['checked'] : element['value'];
|
||||||
return new EventForDotNet<UIChangeEventArgs>('change', { type: event.type, value: newValue });
|
return new EventForDotNet<UIChangeEventArgs>('change', { type: event.type, value: newValue });
|
||||||
|
|
@ -36,7 +42,7 @@ export class EventForDotNet<TData extends UIEventArgs> {
|
||||||
case 'keydown':
|
case 'keydown':
|
||||||
case 'keyup':
|
case 'keyup':
|
||||||
case 'keypress':
|
case 'keypress':
|
||||||
return new EventForDotNet<UIKeyboardEventArgs>('keyboard', parseKeyboardEvent(<KeyboardEvent>event));
|
return new EventForDotNet<UIKeyboardEventArgs>('keyboard', parseKeyboardEvent(event as KeyboardEvent));
|
||||||
|
|
||||||
case 'contextmenu':
|
case 'contextmenu':
|
||||||
case 'click':
|
case 'click':
|
||||||
|
|
@ -46,10 +52,10 @@ export class EventForDotNet<TData extends UIEventArgs> {
|
||||||
case 'mousedown':
|
case 'mousedown':
|
||||||
case 'mouseup':
|
case 'mouseup':
|
||||||
case 'dblclick':
|
case 'dblclick':
|
||||||
return new EventForDotNet<UIMouseEventArgs>('mouse', parseMouseEvent(<MouseEvent>event));
|
return new EventForDotNet<UIMouseEventArgs>('mouse', parseMouseEvent(event as MouseEvent));
|
||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
return new EventForDotNet<UIErrorEventArgs>('error', parseErrorEvent(<ErrorEvent>event));
|
return new EventForDotNet<UIErrorEventArgs>('error', parseErrorEvent(event as ErrorEvent));
|
||||||
|
|
||||||
case 'loadstart':
|
case 'loadstart':
|
||||||
case 'timeout':
|
case 'timeout':
|
||||||
|
|
@ -57,7 +63,7 @@ export class EventForDotNet<TData extends UIEventArgs> {
|
||||||
case 'load':
|
case 'load':
|
||||||
case 'loadend':
|
case 'loadend':
|
||||||
case 'progress':
|
case 'progress':
|
||||||
return new EventForDotNet<UIProgressEventArgs>('progress', parseProgressEvent(<ProgressEvent>event));
|
return new EventForDotNet<UIProgressEventArgs>('progress', parseProgressEvent(event as ProgressEvent));
|
||||||
|
|
||||||
case 'touchcancel':
|
case 'touchcancel':
|
||||||
case 'touchend':
|
case 'touchend':
|
||||||
|
|
@ -65,7 +71,7 @@ export class EventForDotNet<TData extends UIEventArgs> {
|
||||||
case 'touchenter':
|
case 'touchenter':
|
||||||
case 'touchleave':
|
case 'touchleave':
|
||||||
case 'touchstart':
|
case 'touchstart':
|
||||||
return new EventForDotNet<UITouchEventArgs>('touch', parseTouchEvent(<TouchEvent>event));
|
return new EventForDotNet<UITouchEventArgs>('touch', parseTouchEvent(event as TouchEvent));
|
||||||
|
|
||||||
case 'gotpointercapture':
|
case 'gotpointercapture':
|
||||||
case 'lostpointercapture':
|
case 'lostpointercapture':
|
||||||
|
|
@ -77,11 +83,11 @@ export class EventForDotNet<TData extends UIEventArgs> {
|
||||||
case 'pointerout':
|
case 'pointerout':
|
||||||
case 'pointerover':
|
case 'pointerover':
|
||||||
case 'pointerup':
|
case 'pointerup':
|
||||||
return new EventForDotNet<UIPointerEventArgs>('pointer', parsePointerEvent(<PointerEvent>event));
|
return new EventForDotNet<UIPointerEventArgs>('pointer', parsePointerEvent(event as PointerEvent));
|
||||||
|
|
||||||
case 'wheel':
|
case 'wheel':
|
||||||
case 'mousewheel':
|
case 'mousewheel':
|
||||||
return new EventForDotNet<UIWheelEventArgs>('wheel', parseWheelEvent(<WheelEvent>event));
|
return new EventForDotNet<UIWheelEventArgs>('wheel', parseWheelEvent(event as WheelEvent));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new EventForDotNet<UIEventArgs>('unknown', { type: event.type });
|
return new EventForDotNet<UIEventArgs>('unknown', { type: event.type });
|
||||||
|
|
@ -204,8 +210,38 @@ function parseMouseEvent(event: MouseEvent) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCheckbox(element: Element | null) {
|
function isCheckbox(element: Element | null): boolean {
|
||||||
return element && element.tagName === 'INPUT' && element.getAttribute('type') === 'checkbox';
|
return !!element && element.tagName === 'INPUT' && element.getAttribute('type') === 'checkbox';
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeBasedInputs = [
|
||||||
|
'date',
|
||||||
|
'datetime-local',
|
||||||
|
'month',
|
||||||
|
'time',
|
||||||
|
'week',
|
||||||
|
];
|
||||||
|
|
||||||
|
function isTimeBasedInput(element: Element): element is HTMLInputElement {
|
||||||
|
return timeBasedInputs.indexOf(element.getAttribute('type')!) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeTimeBasedValue(element: HTMLInputElement): string {
|
||||||
|
const value = element.value;
|
||||||
|
const type = element.type;
|
||||||
|
switch (type) {
|
||||||
|
case 'date':
|
||||||
|
case 'datetime-local':
|
||||||
|
case 'month':
|
||||||
|
return value;
|
||||||
|
case 'time':
|
||||||
|
return value.length === 5 ? value + ':00' : value; // Convert hh:mm to hh:mm:00
|
||||||
|
case 'week':
|
||||||
|
// For now we are not going to normalize input type week as it is not trivial
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Invalid element type '${type}'.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following interfaces must be kept in sync with the UIEventArgs C# classes
|
// The following interfaces must be kept in sync with the UIEventArgs C# classes
|
||||||
|
|
|
||||||
|
|
@ -172,9 +172,15 @@ namespace Microsoft.AspNetCore.Components.Web
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("checkbox", null, "checked", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("checkbox", null, "checked", "onchange", false, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", "value", "value", "onchange", true, "yyyy-MM-dd")]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", "value", "value", "onchange", true, "yyyy-MM-dd")]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", null, "value", "onchange", true, "yyyy-MM-dd")]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", null, "value", "onchange", true, "yyyy-MM-dd")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("datetime-local", "value", "value", "onchange", true, "yyyy-MM-ddTHH:mm:ss")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("datetime-local", null, "value", "onchange", true, "yyyy-MM-ddTHH:mm:ss")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("month", "value", "value", "onchange", true, "yyyy-MM")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("month", null, "value", "onchange", true, "yyyy-MM")]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", "value", "value", "onchange", true, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", "value", "value", "onchange", true, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", null, "value", "onchange", true, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", null, "value", "onchange", true, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("text", null, "value", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("text", null, "value", "onchange", false, null)]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("time", "value", "value", "onchange", true, "HH:mm:ss")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("time", null, "value", "onchange", true, "HH:mm:ss")]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, "value", "value", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, "value", "value", "onchange", false, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, null, "value", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, null, "value", "onchange", false, null)]
|
||||||
public static partial class BindAttributes
|
public static partial class BindAttributes
|
||||||
|
|
|
||||||
|
|
@ -172,9 +172,15 @@ namespace Microsoft.AspNetCore.Components.Web
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("checkbox", null, "checked", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("checkbox", null, "checked", "onchange", false, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", "value", "value", "onchange", true, "yyyy-MM-dd")]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", "value", "value", "onchange", true, "yyyy-MM-dd")]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", null, "value", "onchange", true, "yyyy-MM-dd")]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("date", null, "value", "onchange", true, "yyyy-MM-dd")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("datetime-local", "value", "value", "onchange", true, "yyyy-MM-ddTHH:mm:ss")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("datetime-local", null, "value", "onchange", true, "yyyy-MM-ddTHH:mm:ss")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("month", "value", "value", "onchange", true, "yyyy-MM")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("month", null, "value", "onchange", true, "yyyy-MM")]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", "value", "value", "onchange", true, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", "value", "value", "onchange", true, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", null, "value", "onchange", true, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("number", null, "value", "onchange", true, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("text", null, "value", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("text", null, "value", "onchange", false, null)]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("time", "value", "value", "onchange", true, "HH:mm:ss")]
|
||||||
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute("time", null, "value", "onchange", true, "HH:mm:ss")]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, "value", "value", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, "value", "value", "onchange", false, null)]
|
||||||
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, null, "value", "onchange", false, null)]
|
[Microsoft.AspNetCore.Components.BindInputElementAttribute(null, null, "value", "onchange", false, null)]
|
||||||
public static partial class BindAttributes
|
public static partial class BindAttributes
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,20 @@ namespace Microsoft.AspNetCore.Components.Web
|
||||||
[BindInputElement("date", null, "value", "onchange", isInvariantCulture: true, format: "yyyy-MM-dd")]
|
[BindInputElement("date", null, "value", "onchange", isInvariantCulture: true, format: "yyyy-MM-dd")]
|
||||||
[BindInputElement("date", "value", "value", "onchange", isInvariantCulture: true, format: "yyyy-MM-dd")]
|
[BindInputElement("date", "value", "value", "onchange", isInvariantCulture: true, format: "yyyy-MM-dd")]
|
||||||
|
|
||||||
|
// type="datetime-local" is invariant culture with a specific format.
|
||||||
|
// See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings for details.
|
||||||
|
[BindInputElement("datetime-local", null, "value", "onchange", isInvariantCulture: true, format: "yyyy-MM-ddTHH:mm:ss")]
|
||||||
|
[BindInputElement("datetime-local", "value", "value", "onchange", isInvariantCulture: true, format: "yyyy-MM-ddTHH:mm:ss")]
|
||||||
|
|
||||||
|
// type="month" is invariant culture with a specific format.
|
||||||
|
// See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings for details.
|
||||||
|
[BindInputElement("month", null, "value", "onchange", isInvariantCulture: true, format: "yyyy-MM")]
|
||||||
|
[BindInputElement("month", "value", "value", "onchange", isInvariantCulture: true, format: "yyyy-MM")]
|
||||||
|
|
||||||
|
// type="time" is invariant culture with a specific format.
|
||||||
|
[BindInputElement("time", null, "value", "onchange", isInvariantCulture: true, format: "HH:mm:ss")]
|
||||||
|
[BindInputElement("time", "value", "value", "onchange", isInvariantCulture: true, format: "HH:mm:ss")]
|
||||||
|
|
||||||
[BindElement("select", null, "value", "onchange")]
|
[BindElement("select", null, "value", "onchange")]
|
||||||
[BindElement("textarea", null, "value", "onchange")]
|
[BindElement("textarea", null, "value", "onchange")]
|
||||||
public static class BindAttributes
|
public static class BindAttributes
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
using BasicTestApp;
|
using BasicTestApp;
|
||||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||||
using Microsoft.AspNetCore.E2ETesting;
|
using Microsoft.AspNetCore.E2ETesting;
|
||||||
|
using Moq;
|
||||||
using OpenQA.Selenium;
|
using OpenQA.Selenium;
|
||||||
using OpenQA.Selenium.Support.UI;
|
using OpenQA.Selenium.Support.UI;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -1018,5 +1020,263 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
Browser.Equal(expected.DateTime, () => DateTimeOffset.Parse(boundValue.Text).DateTime);
|
Browser.Equal(expected.DateTime, () => DateTimeOffset.Parse(boundValue.Text).DateTime);
|
||||||
Assert.Equal(expected.DateTime, DateTimeOffset.Parse(mirrorValue.GetAttribute("value")).DateTime);
|
Assert.Equal(expected.DateTime, DateTimeOffset.Parse(mirrorValue.GetAttribute("value")).DateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindDateTimeLocalTextboxDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("datetime-local-textbox-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("datetime-local-textbox-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("datetime-local-textbox-datetime-mirror"));
|
||||||
|
var expected = new DateTime(1985, 3, 4);
|
||||||
|
Assert.Equal(expected, DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Clear textbox; value updates to 01/01/0001 because that's the default
|
||||||
|
target.Clear();
|
||||||
|
expected = default;
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#datetime-local-textbox-datetime", "2000-01-02T04:05:06");
|
||||||
|
expected = new DateTime(2000, 1, 2, 04, 05, 06);
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindDateTimeLocalTextboxNullableDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("datetime-local-textbox-nullable-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("datetime-local-textbox-nullable-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("datetime-local-textbox-nullable-datetime-mirror"));
|
||||||
|
Assert.Equal(string.Empty, target.GetAttribute("value"));
|
||||||
|
Assert.Equal(string.Empty, boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
Browser.Equal("", () => boundValue.Text);
|
||||||
|
Assert.Equal("", mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#datetime-local-textbox-nullable-datetime", "2000-01-02T04:05:06");
|
||||||
|
var expected = new DateTime(2000, 1, 2, 04, 05, 06);
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
target.SendKeys("\t");
|
||||||
|
Browser.Equal(string.Empty, () => boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindMonthTextboxDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("month-textbox-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("month-textbox-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("month-textbox-datetime-mirror"));
|
||||||
|
var expected = new DateTime(1985, 3, 1);
|
||||||
|
Assert.Equal(expected, DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
// When the value gets displayed the first time it gets truncated to the 1st day,
|
||||||
|
// until there is no change the bound value doesn't get updated.
|
||||||
|
Assert.Equal(expected.AddDays(3), DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected.AddDays(3), DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Clear textbox; value updates to 01/01/0001 because that's the default
|
||||||
|
target.Clear();
|
||||||
|
expected = default;
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#month-textbox-datetime", "2000-02");
|
||||||
|
expected = new DateTime(2000, 2, 1);
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindMonthTextboxNullableDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("month-textbox-nullable-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("month-textbox-nullable-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("month-textbox-nullable-datetime-mirror"));
|
||||||
|
Assert.Equal(string.Empty, target.GetAttribute("value"));
|
||||||
|
Assert.Equal(string.Empty, boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
Browser.Equal("", () => boundValue.Text);
|
||||||
|
Assert.Equal("", mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#month-textbox-nullable-datetime", "2000-02");
|
||||||
|
var expected = new DateTime(2000, 2, 1);
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
target.SendKeys("\t");
|
||||||
|
Browser.Equal(string.Empty, () => boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindTimeTextboxDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("time-textbox-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("time-textbox-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("time-textbox-datetime-mirror"));
|
||||||
|
var expected = DateTime.Now.Date.AddHours(8).AddMinutes(5);
|
||||||
|
Assert.Equal(expected, DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Clear textbox; value updates to 00:00 because that's the default
|
||||||
|
target.Clear();
|
||||||
|
expected = default;
|
||||||
|
Browser.Equal(DateTime.Now.Date, () => DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
Assert.Equal(default, DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(default, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#time-textbox-datetime", "04:05");
|
||||||
|
expected = DateTime.Now.Date.Add(new TimeSpan(4, 5, 0));
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindTimeTextboxNullableDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("time-textbox-nullable-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("time-textbox-nullable-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("time-textbox-nullable-datetime-mirror"));
|
||||||
|
Assert.Equal(string.Empty, target.GetAttribute("value"));
|
||||||
|
Assert.Equal(string.Empty, boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
Browser.Equal("", () => boundValue.Text);
|
||||||
|
Assert.Equal("", mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#time-textbox-nullable-datetime", "05:06");
|
||||||
|
var expected = DateTime.Now.Date.Add(new TimeSpan(05, 06, 0));
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
target.SendKeys("\t");
|
||||||
|
Browser.Equal(string.Empty, () => boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindTimeStepTextboxDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("time-step-textbox-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("time-step-textbox-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("time-step-textbox-datetime-mirror"));
|
||||||
|
var expected = DateTime.Now.Date.Add(new TimeSpan(8, 5, 30));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Clear textbox; value updates to 00:00 because that's the default
|
||||||
|
target.Clear();
|
||||||
|
expected = default;
|
||||||
|
Browser.Equal(DateTime.Now.Date, () => DateTime.Parse(target.GetAttribute("value")));
|
||||||
|
Assert.Equal(default, DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(default, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#time-step-textbox-datetime", "04:05:06");
|
||||||
|
expected = DateTime.Now.Date.Add(new TimeSpan(4, 5, 6));
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
|
||||||
|
// Blazor have different formatting behaviour by default.
|
||||||
|
[Fact]
|
||||||
|
public void CanBindTimeStepTextboxNullableDateTime()
|
||||||
|
{
|
||||||
|
var target = Browser.FindElement(By.Id("time-step-textbox-nullable-datetime"));
|
||||||
|
var boundValue = Browser.FindElement(By.Id("time-step-textbox-nullable-datetime-value"));
|
||||||
|
var mirrorValue = Browser.FindElement(By.Id("time-step-textbox-nullable-datetime-mirror"));
|
||||||
|
Assert.Equal(string.Empty, target.GetAttribute("value"));
|
||||||
|
Assert.Equal(string.Empty, boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
Browser.Equal("", () => boundValue.Text);
|
||||||
|
Assert.Equal("", mirrorValue.GetAttribute("value"));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
// We have to do it this way because the browser gets in the way when sending keys to the input
|
||||||
|
// element directly.
|
||||||
|
ApplyInputValue("#time-step-textbox-nullable-datetime", "05:06");
|
||||||
|
var expected = DateTime.Now.Date.Add(new TimeSpan(05, 06, 0));
|
||||||
|
Browser.Equal(expected, () => DateTime.Parse(boundValue.Text));
|
||||||
|
Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value")));
|
||||||
|
|
||||||
|
// Modify target; verify value is updated and that textboxes linked to the same data are updated
|
||||||
|
target.Clear();
|
||||||
|
target.SendKeys("\t");
|
||||||
|
Browser.Equal(string.Empty, () => boundValue.Text);
|
||||||
|
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies an input through javascript to datetime-local/month/time controls.
|
||||||
|
private void ApplyInputValue(string cssSelector, string value)
|
||||||
|
{
|
||||||
|
// It's very difficult to enter an invalid value into an <input type=date>, because
|
||||||
|
// most combinations of keystrokes get normalized to something valid. Additionally,
|
||||||
|
// using Selenium's SendKeys interacts unpredictably with this normalization logic,
|
||||||
|
// most likely based on timings. As a workaround, use JS to apply the values. This
|
||||||
|
// should only be used when strictly necessary, as it doesn't represent actual user
|
||||||
|
// interaction as authentically as SendKeys in other cases.
|
||||||
|
var javascript = (IJavaScriptExecutor)Browser;
|
||||||
|
javascript.ExecuteScript(
|
||||||
|
$"var elem = document.querySelector('{cssSelector}');"
|
||||||
|
+ $"elem.value = '{value}';"
|
||||||
|
+ "elem.dispatchEvent(new KeyboardEvent('change'));");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,63 @@
|
||||||
<span id="select-markup-box-value">@selectMarkupValue</span>
|
<span id="select-markup-box-value">@selectMarkupValue</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h2>Time related inputs (datetime-local, month, time)</h2>
|
||||||
|
<h3>datetime-local</h3>
|
||||||
|
<p>
|
||||||
|
DateTime:
|
||||||
|
<input id="datetime-local-textbox-datetime" @bind="dateTimeLocalTextboxDateTimeValue" type="datetime-local" />
|
||||||
|
<span id="datetime-local-textbox-datetime-value">@dateTimeLocalTextboxDateTimeValue</span>
|
||||||
|
<input id="datetime-local-textbox-datetime-mirror" @bind="dateTimeLocalTextboxDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nullable DateTime:
|
||||||
|
<input id="datetime-local-textbox-nullable-datetime" @bind-value="dateTimeLocalTextboxNullableDateTimeValue" type="datetime-local" />
|
||||||
|
<span id="datetime-local-textbox-nullable-datetime-value">@dateTimeLocalTextboxNullableDateTimeValue</span>
|
||||||
|
<input id="datetime-local-textbox-nullable-datetime-mirror" @bind="dateTimeLocalTextboxNullableDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>month</h3>
|
||||||
|
<p>
|
||||||
|
DateTime:
|
||||||
|
<input id="month-textbox-datetime" @bind="monthTextboxDateTimeValue" type="month" />
|
||||||
|
<span id="month-textbox-datetime-value">@monthTextboxDateTimeValue</span>
|
||||||
|
<input id="month-textbox-datetime-mirror" @bind="monthTextboxDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nullable DateTime:
|
||||||
|
<input id="month-textbox-nullable-datetime" @bind-value="monthTextboxNullableDateTimeValue" type="month" />
|
||||||
|
<span id="month-textbox-nullable-datetime-value">@monthTextboxNullableDateTimeValue</span>
|
||||||
|
<input id="month-textbox-nullable-datetime-mirror" @bind="monthTextboxNullableDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>time</h3>
|
||||||
|
<p>
|
||||||
|
DateTime:
|
||||||
|
<input id="time-textbox-datetime" @bind="timeTextboxDateTimeValue" type="time" />
|
||||||
|
<span id="time-textbox-datetime-value">@timeTextboxDateTimeValue</span>
|
||||||
|
<input id="time-textbox-datetime-mirror" @bind="timeTextboxDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nullable DateTime:
|
||||||
|
<input id="time-textbox-nullable-datetime" @bind-value="timeTextboxNullableDateTimeValue" type="time" />
|
||||||
|
<span id="time-textbox-nullable-datetime-value">@timeTextboxNullableDateTimeValue</span>
|
||||||
|
<input id="time-textbox-nullable-datetime-mirror" @bind="timeTextboxNullableDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>time with step (supports seconds)</h3>
|
||||||
|
<p>
|
||||||
|
DateTime:
|
||||||
|
<input id="time-step-textbox-datetime" @bind="timeStepTextboxDateTimeValue" type="time" step="1" />
|
||||||
|
<span id="time-step-textbox-datetime-value">@timeStepTextboxDateTimeValue</span>
|
||||||
|
<input id="time-step-textbox-datetime-mirror" @bind="timeStepTextboxDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nullable DateTime:
|
||||||
|
<input id="time-step-textbox-nullable-datetime" @bind-value="timeStepTextboxNullableDateTimeValue" type="time" step="1" />
|
||||||
|
<span id="time-step-textbox-nullable-datetime-value">@timeStepTextboxNullableDateTimeValue</span>
|
||||||
|
<input id="time-step-textbox-nullable-datetime-mirror" @bind="timeStepTextboxNullableDateTimeValue" readonly />
|
||||||
|
</p>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
string textboxInitiallyBlankValue = null;
|
string textboxInitiallyBlankValue = null;
|
||||||
string textboxInitiallyPopulatedValue = "Hello";
|
string textboxInitiallyPopulatedValue = "Hello";
|
||||||
|
|
@ -297,6 +354,18 @@
|
||||||
DateTime textboxDateTimeFormatInvalidValue = new DateTime(1985, 3, 4);
|
DateTime textboxDateTimeFormatInvalidValue = new DateTime(1985, 3, 4);
|
||||||
DateTimeOffset? textboxNullableDateTimeOffsetFormatInvalidValue = null;
|
DateTimeOffset? textboxNullableDateTimeOffsetFormatInvalidValue = null;
|
||||||
|
|
||||||
|
DateTime dateTimeLocalTextboxDateTimeValue = new DateTime(1985, 3, 4);
|
||||||
|
DateTime? dateTimeLocalTextboxNullableDateTimeValue = null;
|
||||||
|
|
||||||
|
DateTime monthTextboxDateTimeValue = new DateTime(1985, 3, 4);
|
||||||
|
DateTime? monthTextboxNullableDateTimeValue = null;
|
||||||
|
|
||||||
|
DateTime timeTextboxDateTimeValue = DateTime.Now.Date.Add(new TimeSpan(8, 5, 0));
|
||||||
|
DateTime? timeTextboxNullableDateTimeValue = null;
|
||||||
|
|
||||||
|
DateTime timeStepTextboxDateTimeValue = DateTime.Now.Date.Add(new TimeSpan(8, 5, 30));
|
||||||
|
DateTime? timeStepTextboxNullableDateTimeValue = null;
|
||||||
|
|
||||||
bool includeFourthOption = false;
|
bool includeFourthOption = false;
|
||||||
enum SelectableValue { First, Second, Third, Fourth }
|
enum SelectableValue { First, Second, Third, Fourth }
|
||||||
SelectableValue selectValue = SelectableValue.Second;
|
SelectableValue selectValue = SelectableValue.Second;
|
||||||
|
|
|
||||||
|
|
@ -103,4 +103,13 @@
|
||||||
|
|
||||||
DateTime inputDateDateTime = new DateTime(1985, 3, 4);
|
DateTime inputDateDateTime = new DateTime(1985, 3, 4);
|
||||||
DateTimeOffset inputDateDateTimeOffset = new DateTimeOffset(new DateTime(1985, 3, 4));
|
DateTimeOffset inputDateDateTimeOffset = new DateTimeOffset(new DateTime(1985, 3, 4));
|
||||||
|
|
||||||
|
DateTime inputTypeDateTimeLocalDateTime = new DateTime(1985, 3, 4);
|
||||||
|
DateTimeOffset inputTypeDateTimeLocalDateTimeOffset = new DateTimeOffset(new DateTime(1985, 3, 4));
|
||||||
|
|
||||||
|
DateTime inputTypeMonthDateTime = new DateTime(1985, 3, 4);
|
||||||
|
DateTimeOffset inputTypeMonthDateTimeOffset = new DateTimeOffset(new DateTime(1985, 3, 4));
|
||||||
|
|
||||||
|
DateTime inputTypeTimeDateTime = new DateTime(1985, 3, 4);
|
||||||
|
DateTimeOffset inputTypeTimeDateTimeOffset = new DateTimeOffset(new DateTime(1985, 3, 4));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,14 @@ namespace OpenQA.Selenium
|
||||||
public class BrowserAssertFailedException : XunitException
|
public class BrowserAssertFailedException : XunitException
|
||||||
{
|
{
|
||||||
public BrowserAssertFailedException(IReadOnlyList<LogEntry> logs, Exception innerException, string screenShotPath)
|
public BrowserAssertFailedException(IReadOnlyList<LogEntry> logs, Exception innerException, string screenShotPath)
|
||||||
: base(BuildMessage(logs, screenShotPath), innerException)
|
: base(BuildMessage(innerException, logs, screenShotPath), innerException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string BuildMessage(IReadOnlyList<LogEntry> logs, string screenShotPath) =>
|
private static string BuildMessage(Exception innerException, IReadOnlyList<LogEntry> logs, string screenShotPath) =>
|
||||||
|
innerException.ToString() + Environment.NewLine +
|
||||||
(File.Exists(screenShotPath) ? $"Screen shot captured at '{screenShotPath}'" + Environment.NewLine : "") +
|
(File.Exists(screenShotPath) ? $"Screen shot captured at '{screenShotPath}'" + Environment.NewLine : "") +
|
||||||
(logs.Count > 0 ? "Encountered browser errors" : "No browser errors found") + " while running the assertion." + Environment.NewLine +
|
(logs.Count > 0 ? "Encountered browser logs" : "No browser logs found") + " while running the assertion." + Environment.NewLine +
|
||||||
string.Join(Environment.NewLine, logs);
|
string.Join(Environment.NewLine, logs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue