Repository: dotnet/aspnetcore
File: src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/test/GridRaceConditionTest.cs (C#)

Code before:
    {
        var moduleLoadCompletion = new TaskCompletionSource();
        var moduleImportStarted = new TaskCompletionSource();
        var testJsRuntime = new TestJsRuntime(moduleLoadCompletion, moduleImportStarted);
        var serviceProvider = new ServiceCollection()
            .AddSingleton<IJSRuntime>(testJsRuntime)
            .BuildServiceProvider();
        var renderer = new TestRenderer(serviceProvider);

        var testComponent = new FailingGridTestComponent();

        var componentId = renderer.AssignRootComponentId(testComponent);
        renderer.RenderRootComponent(componentId);

        // Wait until JS import has started but not completed
        await moduleImportStarted.Task;

        // Dispose component while JS module loading is pending
        testJsRuntime.MarkDisposed();
        await renderer.DisposeAsync();

        // Complete the JS module loading - this allows the FailingQuickGrid's OnAfterRenderAsync to continue
        // and demonstrate the race condition by calling init after disposal
        moduleLoadCompletion.SetResult();

        // Wait for OnAfterRenderAsync to complete - deterministic timing instead of arbitrary delay
        var failingGrid = testComponent.FailingQuickGrid;
        await failingGrid.OnAfterRenderCompleted;

        // Assert that init WAS called after disposal
        // The FailingQuickGrid's OnAfterRenderAsync should have called init despite being disposed
        // The FailingQuickGrid should not have disposed of JsModule 
        Assert.True(testJsRuntime.InitWasCalledAfterDisposal,
            $"FailingQuickGrid should call init after disposal, demonstrating the race condition bug. " +
            $"InitWasCalledAfterDisposal: {testJsRuntime.InitWasCalledAfterDisposal}, " +
            $"DisposeAsyncWasCalled: {failingGrid.DisposeAsyncWasCalled}, " +
            $"_disposeBool is false: {failingGrid.IsWasDisposedFalse()}");
        Assert.False(testJsRuntime.JsModuleDisposed);
    }
}

internal class Person
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

internal abstract class BaseTestComponent<TGrid> : ComponentBase
    where TGrid : ComponentBase
{
    [Inject] public IJSRuntime JSRuntime { get; set; } = default!;

Review comment (suggestion):
```suggestion
        // Wait until after OnAfterRenderAsync has completed, to make sure jsmodule import started and the reported issue is reproduced
```

Clarified offline.

Code after:
    {
        var moduleLoadCompletion = new TaskCompletionSource();
        var moduleImportStarted = new TaskCompletionSource();
        var testJsRuntime = new TestJsRuntime(moduleLoadCompletion, moduleImportStarted);
        var serviceProvider = new ServiceCollection()
            .AddSingleton<IJSRuntime>(testJsRuntime)
            .BuildServiceProvider();
        var renderer = new TestRenderer(serviceProvider);

        var testComponent = new FailingGridTestComponent();

        var componentId = renderer.AssignRootComponentId(testComponent);
        renderer.RenderRootComponent(componentId);

        // Wait until JS import has started but not completed
        await moduleImportStarted.Task;

        // Dispose component while JS module loading is pending
        testJsRuntime.MarkDisposed();
        await renderer.DisposeAsync();

        // Complete the JS module loading - this allows the FailingQuickGrid's OnAfterRenderAsync to continue
        // and demonstrate the race condition by calling init after disposal
        moduleLoadCompletion.SetResult();

        // Wait until after OnAfterRenderAsync has completed, to make sure jsmodule import started and the reported issue is reproduced
        var failingGrid = testComponent.FailingQuickGrid;
        await failingGrid.OnAfterRenderCompleted;

        // Assert that init WAS called after disposal
        // The FailingQuickGrid's OnAfterRenderAsync should have called init despite being disposed
        // The FailingQuickGrid should not have disposed of JsModule 
        Assert.True(testJsRuntime.InitWasCalledAfterDisposal,
            $"FailingQuickGrid should call init after disposal, demonstrating the race condition bug. " +
            $"InitWasCalledAfterDisposal: {testJsRuntime.InitWasCalledAfterDisposal}, " +
            $"DisposeAsyncWasCalled: {failingGrid.DisposeAsyncWasCalled}, " +
            $"_disposeBool is false: {failingGrid.IsWasDisposedFalse()}");
        Assert.False(testJsRuntime.JsModuleDisposed);
    }
}

internal class Person
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

internal abstract class BaseTestComponent<TGrid> : ComponentBase
    where TGrid : ComponentBase
{
    [Inject] public IJSRuntime JSRuntime { get; set; } = default!;