microsoft / playwright-dotnet

.NET version of the Playwright testing and automation library.
https://playwright.dev/dotnet/
MIT License
2.47k stars 235 forks source link

Paths.GetExecutablePath fails in .NET 6 single-file executables #1527

Closed pkanavos closed 2 years ago

pkanavos commented 3 years ago

In .NET 6 single-file applications, Assembly.Location is documented to return an empty string. This causes the Microsoft.Playwright.Helpers.Paths.GetExecutablePath() method called by Playwright.CreateAsyn() to fail with the following exception:

Unhandled exception: System.ArgumentException: The path is empty. (Parameter 'path')
   at System.IO.Path.GetFullPath(String path) in System.Private.CoreLib.dll:token 0x60058bc+0x2a
   at System.IO.FileInfo..ctor(String originalPath, String fullPath, String fileName, Boolean isNormalized) in System.IO.FileSystem.dll:token 0x600014d+0x24
   at System.IO.FileInfo..ctor(String fileName) in System.IO.FileSystem.dll:token 0x600014c+0x0
   at Microsoft.Playwright.Helpers.Paths.GetExecutablePath() in Microsoft.Playwright.dll:token 0x6000dc9+0x10
   at Microsoft.Playwright.Transport.Connection.GetProcess() in Microsoft.Playwright.dll:token 0x6000b38+0x5
   at Microsoft.Playwright.Transport.Connection..ctor() in Microsoft.Playwright.dll:token 0x6000b2c+0xa8
   at Microsoft.Playwright.Playwright.CreateAsync() in Microsoft.Playwright.dll:token 0x6000764+0xa

The method is trying to identify Playwright's path by checking the library's location. In this case though, Assembly.Location will be empty:

    internal static string GetExecutablePath()       
   {            
       var assembly = typeof(Playwright).Assembly;            
       string driversPath = new FileInfo(assembly.Location).Directory.FullName;
       string executableFile = GetPath(driversPath);
        if (File.Exists(executableFile))
        {
            return executableFile;
        }
        throw new PlaywrightException($@"Driver not found: {executableFile}");
    }

In this case, the playwright.ps1 script will also fail because it tries to load the Playwright assembly explicitly:

[Reflection.Assembly]::LoadFile("$($PSScriptRoot)/Microsoft.Playwright.dll")
[Microsoft.Playwright.Program]::Main($args)

https://github.com/microsoft/playwright-dotnet/blob/1b18e16cd3ac16188d8e52a8c1f427e98c18aff3/src/Playwright/Helpers/Paths.cs#L37

avodovnik commented 3 years ago

Thanks for brining this up!

Since .NET 6 is still a preview, it's not a scenario we looked at yet. It sounds like it might require a completely different approach to packaging the drivers, though. That said, PR #1520 might provide a reasonably sufficient workaround. I've added an additional guard to the location check though.

eddietisma commented 3 years ago

I have the same issue when using the PublishSingleFile option when publishing a .NET 5 project. In v1.9.2 this was working without any issues.

dotnet publish "src/MyProject.csproj" -c Release -r linux-x64 -p:PublishSingleFile=true --self-contained false -o /publish

Both v1.12.2 and v1.13.0-next-1 throws System.ArgumentException: The path is empty.

avodovnik commented 3 years ago

@eddietisma I suspect the problem is that we reset the location of the assembly, if we can't find Microsoft.Playwright.dll in the same place - which we need to provide CLI functionality. #1603 will likely fix it, assuming we go ahead and merge it.

As a workaround, though, you can place a file and call it Microsoft.Playwright.dll next to the published single file, and it should work.

mxschmitt commented 2 years ago

Closing as part of the triage process since it seemed stale or did not get enough upvotes in the past. Please create a new issue with a detailed reproducible or feature request if you still face issues.