@ledsun blog

無味の味は佳境に入らざればすなわち知れず

ruby.wasmのビルドの並列オプションの効果

ruby.wasmのコミットのなかで、make を並列実行する -j オプションを追加するコミットがありました。

build: use -jN option for "make install" in crossruby · ruby/ruby.wasm@02e84be · GitHub

本当に並列化されているのか観察してみました。

Rubyをビルドしてるときのスクリーンショット

RubyをビルドしているときはCPUが全部動いています。 並列化が効いているようです。

OpenSSLをビルドしてるときのスクリーンショット

OpenSSLをビルドするときはCPUは大分休んでいます。 何かしら理由があって1つだけに絞っているようです。 https://github.com/ruby/ruby.wasm/blob/3122466a0f12e97ec453f70949b5c0117f92926c/lib/ruby_wasm/build/product/openssl.rb#L83 で明示的に1を指定しています。

wasmをビルドしてるときのスクリーンショット

wasmをビルドしているときもCPUが全部動いています。 並列化が効いているようです。

全部で830秒、13分でした。 コアの沢山あるCPUでビルドするともっと速くなるのでしょうか?興味深いです。

Playwrightでリダイレクトの動作をテストで確認する

PlaywrightのNewwork mockingをつかってみる - @ledsun blog ではテスト用のUIを使ってリダイレクトの動作を確認しました。 テストコードで確認してみましょう。

import { test, expect } from '@playwright/test';

test.beforeEach(async ({ context }) => {
  await context.route(/redirect_to/, route => route.fulfill({
    status: 301,
    headers: {
      location: 'https://playwright.dev/'
    }
  }));
});

test('has title', async ({ page }) => {
  await page.goto('https://playwright.dev/');

  const url = await page.evaluate(async () => {
    const response = await fetch('https://example.com/redirect_to')
    return response.url
  })

  expect(url).toBe('https://playwright.dev/')
});

page.evaluateをつかって、ページ内でJavaScriptを実行します。 fetch('https://example.com/redirect_to')を実行して、Response#urlがリダレクト先のURLになっているか確認します。

前回と比べて次の点が変わっています

  • 意図しないファイルをリダイレクトしないように、リダイレクト用のキーワードをredirect_toに変更
  • httpsのページからfetchするために、fetchの宛先をhttpsに変更

Playwrightでローカルファイルを読み込む

次のようなindex.htmlを用意します。

<html>
  <title>Playwright</title>
</html>

テストは次のように書きます。

import path from 'path';
import { test, expect } from '@playwright/test';

test('has title', async ({ page }) => {
  const htmlPath = `file://${path.join(__dirname, `./index.html`)}`;
  await page.goto(htmlPath);

  // Expect a title "to contain" a substring.
  await expect(page).toHaveTitle(/Playwright/);
});

page.goto の引数に file:// で始まるファイルのパスを指定します。 index.htmlのtitleタグの要素を変えると、テストが失敗します。 期待通りにローカルファイルを読み込めているようです。

参考

Playwrightでローカルファイルを読み込む #TypeScript - Qiita

Amazon Linuxに関する調査メモ

Amazon Linux 2では、Node.jsの新しいバージョンをインストールできなかったり、少しずつ時代遅れ感が出ています。 では、「Amazon Linux 2023にすればいいのか?」というと、どうもそう簡単ではないようです。

これは、アップデートに挑戦して苦戦した話ではなく、アップデートするまえにどんな感じなのか軽く調べてみたメモです。

Amazon Linux 2023を触ってみて質問がありそうなことをまとめてみました。 | ソフトウェア開発のギークフィード

Fedoraの複数バージョンのコンポーネント+アルファをAWSがメンテして一般公開版を出したということになりますので、独自ディストリビューションの公開と維持に、よっぽどの稼働とコストがかかっている

自分の場合は、AWS EC2を使ってもありきたりなWebサーバーを起動したいだけです。 頑張って Amazon Linux を使うべきなのでしょうか? RedHat系のディストリビューションに乗り換えた方が良いのではないでしょうか?

今まで、開発環境では Ubuntu/Debian を使って生ききたので、RedHat系のディストリビューションには詳しくありません。 RedHat系の無料ディストリビューションと言えばCentOSですが、来年で終了するようです。

CentOS Linux のサポート終了について | Red Hat

CentOS Linux 7 のサポート期限は、2024 年 6 月 30 日に終了 (EOL) となります。

後継となるディストリビューションには何があるのか調べてみると、Alma LinuxとRocky Linuxがあるようです。 どうやらAlma Linuxの方が、少し人気があるように思えます。 理由は「企業ではなく非営利団体がメンテナンスしているため、急な方針転換が少ないだろう」でした。 では、Alama Linuxを選んでおけば良いかというと・・・

AlmaLinux と Rocky Linux の比較メモ(2023年8月版) | あぱーブログ

2023年6月 Red Hat は git.centos.org リポジトリの更新を停止したため、このリポジトリからソースコードを取得してビルドしていた AlmaLinux や Rocky Linux など、RHELクローンと呼ばれる Linux ディストリビューションは、ソースコードの取得先の変更を求められています。そのため、今後の各社の動きに注目しておく必要があります。

まだもうちょっと様子を見た方が良いようです。 個人的には急いでいないですし、Amazon Linux 2023でもよいので、もう少し様子を見ます。

参考

名古屋に行った

名古屋出張があったので、翌日を休みにして名古屋駅周辺を半日散歩しました。

名古屋といえばナナちゃん人形

大きな教会がありました。

在日大韓基督教教科書という建物らしいです。 サムネイルのモアレがすごいです。

ノリタケの森

無料で入れました。 公園や美術展示がありました。

お昼ご飯も食べました。

食器はノリタケ製です。 200万円するティーセットも売っていました。どんな方が買うんでしょうね?

その他

名古屋のモード学園

新宿のコクーンタワーの兄弟。 こっちもすごいです。

コレクトマーク

ダクトが美しいです。

PlaywrightのNewwork mockingをつかってみる

Playwrightをつかってみる - @ledsun blog でPlaywrightが最低限動く環境ができました。 リダイレクトレスポンスを扱うテストを書こうと思います。

Playwrightには Netwrok mockingという機能があります。 実行するテストからのHTTPリクエストをモックして書き換えることができます。

サンプルのコードは次のように動きます。

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: https://playwright.dev/
    Server->>Client: 200

これにモックを挟んでみます。

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: Request http://exapmle.com/redirect
    Server->>Client: 301
    Client->>Server: https://playwright.dev/
    Server->>Client: 200 OK

最初の宛先を http://exapmle.com/redirect に変更します。 このリクエストをモックして301 レスポンスを返します。 https://playwright.dev/ にリダイレクトします。

test.beforeEach(async ({ context }) => {
  await context.route(/redirect/, route => route.fulfill({
    status: 301,
    headers: {
      location: 'https://playwright.dev/'
    }
  }));
});

リクエストの宛先に redirect を含むときにモックします。 route.fulfill を使ってレスポンスを書き換えます。

テストも次のように書き換えます。

test('has title', async ({ page }) => {
  await page.goto('http://exapmle.com/redirect');

  // Expect a title "to contain" a substring.
  await expect(page).toHaveTitle(/Playwright/);
});

開くURLを http://exapmle.com/redirect に変更します。 テストを実行してみましょう。 npx playwright test --project=chromium --uiでテストを起動します。

PlaywrightのUIを起動した直後のスクリーンショット

次のように選択してテストを実行します。

  • テスト: has title
  • Networkタブ

テスト実行完了後のスクリーンショット

Networkタブをみます。

  1. /redirect にリクエストして 301が返る
  2. / にリクエストする

と、期待通りに動いたことが確認できました。

よく見ると https://playwright.dev/js/redirection.js へのリクエストもリダイレクトされています。

Playwrightをつかってみる

Installation | Playwrightの手順に従って進めます。

npm init playwright@latest

いまはnpm initにプロジェクトテンプレートをつくる機能が組み込まれているようです。

npm-init | npm Docsによると

initializer in this case is an npm package named create-, which will be installed by npm-exec, and then have its main bin executed -- presumably creating or updating package.json and running any other initialization-related operations.

create-<initializer> というパッケージが実行されるそうです。 つまり、create-playwright - npmインストーラーのようです。

これでプロジェクトが作成されます。 次のコマンドで、サンプルのテストコードが実行出来ます。

npx playwright test

僕の環境ではエラーが出ます。

  4 failed
    [firefox] › example.spec.ts:3:5 › has title ────────────────────────────────────────────────────
    [firefox] › example.spec.ts:10:5 › get started link ────────────────────────────────────────────
    [webkit] › example.spec.ts:3:5 › has title ─────────────────────────────────────────────────────
    [webkit] › example.spec.ts:10:5 › get started link ─────────────────────────────────────────────
  2 passed (2.0s)

どうやらブラウザを実行するためのライブラリーが足りないようです。 Chromiumに限定して実行してみましょう。

npx playwright test --project=chromium

成功しました。

Running 2 tests using 2 workers
  2 passed (1.5s)

To open last HTML report run:

  npx playwright show-report

サンプルのテストケースは2つあるようです。 npx playwright show-report コマンドでテスト結果をHTML形式で見れるようです。 成功時はあんまり必要無いですね。 ちなみに、失敗時はデフォルトでHTML形式で表示されます。

リダイレクトレスポンス対応版 require_relativeを組み込んだruby.wasmを実際に動かして、問題を切り分ける

ruby.wasmでrequire_relativeしたときのリダイレクトレスポンスの扱い方 - @ledsun blog に書いたように、HTTPリクエストのリダイレクトレスポンスでURLが変わったら、変更後のURLをロード済みとして扱いたいです。

リダイレクトレスポンス対応版 require_relativeの実装

ruby.wasmのrequire_relativeではfetch APIを使います。 fetch APIからリダイレクト後のURLがとれば良いはずです。

Response.url - Web API | MDN には

url プロパティの値は、あらゆるリダイレクトの後に得られる最終的な URL になります。

と、あります。 これを使えば良さそうです。 次のようなコードを書きました。

response = JS.global.fetch(location.url).await

if response[:status].to_i == 200
  return false if @loaded_urls.include?(response[:url].to_s)

  code = response.text().await.to_s
  eval_code(code, location)

  @loaded_urls << response[:url].to_s
  true
else
  raise LoadError.new "cannot load such url -- #{location.url}"
end

@loaded_urlsresponse.url から取得したURLを保存しておきます。 次のfetchの response.url が、@loaded_urls に含まれていたら、Rubyスクリプトを実行せずにリターンします。 これで良さそうに思います。

実際に動かしてみる

しかし、E2Eテストでは失敗します。 失敗した結果 ruby.wasmのE2Eテストをデバッグする - @ledsun blog をしていました。 また、Response.urlの挙動を確認する - @ledsun blog で、Response.url単体では期待通りに動くことが確認できました。 ということは、ruby.wasmに組み込んでも期待通りに動きそうです。 確かめてみましょう。

次のようなサーバーを用意します。

require 'webrick'

root = File.expand_path 'public' # 静的ファイルが置かれるディレクトリ
server = WEBrick::HTTPServer.new :Port => 3000, :DocumentRoot => root

# リダイレクトの設定
server.mount_proc '/redirect.rb' do |req, res|
  res.status = 302
  res['Location'] = 'app.rb'
end

trap 'INT' do
  server.shutdown
end

server.start

このサーバーはpublicディレクトリにあるファイルを静的配信します。 さらに /redirect.rb にきたリクエストにはリダイレクトレスポンスを返し app.rb に誘導します。

publicディレクトリに、自分でビルドしたruby.wasmを実行する環境をつくる - @ledsun blogの要領で、browser.script.iife.jsruby+stdlib.wsmを用意します。

require_relativeの動作確認する - @ledsun blog の要領でrequire_relativeを使うindex.htmlを用意します。

<html>
  <script src="browser.script.iife.js"></script>
  <script type="text/ruby">
    require 'js/require_remote'

    module Kernel
      alias original_require_relative require_relative

      # The require_relative may be used in the embedded Gem.
      # First try to load from the built-in filesystem, and if that fails,
      # load from the URL.
      def require_relative(path)
        caller_path = caller_locations(1, 1).first.absolute_path || ''
        dir = File.dirname(caller_path)
        file = File.absolute_path(path, dir)

        original_require_relative(file)
      rescue LoadError
        JS::RequireRemote.instance.load(path)
      end
    end
  </script>
  <script type="text/ruby" data-eval="async">
    p require_relative 'redirect'
    p require_relative 'app'
  </script>
</html>

ただし次のようにリダイレクトを交えたrequire_relativeを実行します。

p require_relative 'redirect'
p require_relative 'app'

リダイレクト後の app.rb がロード済みになっているはずです。 その後 app.rb をロードしようとしてもスキップされて欲しいです。 つまり、開発コンソールに、最初にtrueつぎにfalseが表示されてほしいです。 サーバーを起動して動作を確認します。

ruby sever.rb

http://localhost:3000/ を開いて開発コンソールを確認します。

開発コンソールのスクリーンショット

期待通りです。 最初にtrueつぎにfalseが表示されました。

まとめ

わかったことをまとめます。

  1. response.urlはfetchを単体で動かしたときリダイレクト後のURLが取得できる
  2. response.urlをruby.wasm版 require_relative に組み込むと、期待通りに動く
  3. E2Eでは期待通りに動かない
  4. 特にresponse.urlの値がリダイレクト前のまま

以上から、今後はE2Eテストの実行環境を重点的にしらべていくと良さそうです。

ruby.wasmでrequire_relativeしたときのリダイレクトレスポンスの扱い方

require_relativeでリダイレクトレスポンスをサポートしたいと思っています。 たとえば次のようなシナリオです。

  1. require_relative "a" を実行する
  2. fetch('a.rb') を実行する
  3. a.rb にGETリクエストを送る
  4. サーバーから302レスポンスでb.rbが返ってくる
  5. b.rb にGETリクエストを送る
  6. Rubyスクリプトの取得する
  7. Kernel.evalRubyスクリプトを実行する

シーケンス図で次のようになります。

sequenceDiagram
    participant Client
    participant Server

    Client->>Client: require_relative "a"
    Client->>Client: fetch('a.rb') 
    Client->>Server: GET request './a.rb'
    Server->>Client: 302 Response with './b.rb'
    Client->>Server: GET request for './b.rb'
    Server->>Client: Ruby script transfer
    Client->>Client: Kernel.eval Ruby script

4~5のリダイレクトレスポンスに追随してGETリクエストを送る動作はfetch APIがサポートしています。 特に追加で何かする必要はありません。

気にするのは'a.rb'をロードしたつもりが、実際は'b.rb'を読み込んだシチュエーションです。 このあと require_relative "b" を実行したときにRubyスクリプト b.rb を再び実行するかどうかです。

結論としては b.rb を読込済みとし、再び実行しません。 二重読込を防止したいのは、Rubyスクリプトの実体だと考えています。 同じRubyスクリプトを別の名前で require_relaitve したときに、二度実行するのは、ユーザーの意図した動作ではないと考えます。

require_relativeの動作確認する

自分でビルドしたruby.wasmを実行する環境をつくる - @ledsun blog で、ruby.wasmのカスタムビルドを動かせるようになりました。 実装中の require_relative が動くか確かめてみましょう。

次のような index.hml を作成します。

<html>
  <script src="browser.script.iife.js"></script>
  <script type="text/ruby">
    require 'js/require_remote'

    module Kernel
      def require_relative(path) = JS::RequireRemote.instance.load(path)
    end
  </script>
  <script type="text/ruby" data-eval="async">
    require_relative 'app'
  </script>
</html>

現在は、ruby.wasmで Kernel#require_relative 自体を改変しない方針を採っています。 代わりに、ブラウザ上で Kernel#require_relative のように動く、JS::RequireRemote#load *1を作成しています。

ローダー周りのメソッドはパッチを当てられがちです。*2 ユーザーは別の目的で Kernel#require_relative にパッチを当てたいかも知れません。 パッチを当てるかどうかの判断をユーザーに委ねるためです。

require_relativeの読込先として、次のような app.rb を用意します。

puts 'Hello, World!'

前回と同じように Hello, world! が表示されます。

ブラウザの開発コンソールにHello, world!が表示されているスクリーンショット

ちなみに上記の Kenerl#require_relative のパッチは、既存の動作を壊しています。 たとえば、require 'csv'を実行すると次のエラーが起きます。

require 'csv'を実行した時のエラー

csv gemの中のrequire_relative "csv/fields_converter" *3の動作が変わって起きたエラーです。 既存の動作を壊さないさないようにするには、次のように、もう少し複雑なパッチを当てる必要があります。

module Kernel
  alias original_require_relative require_relative

  def require_relative(path)
    caller_path = caller_locations(1, 1).first.absolute_path || ''
    dir = File.dirname(caller_path)
    file = File.absolute_path(path, dir)

    original_require_relative(file)
  rescue LoadError
    JS::RequireRemote.instance.load(path)
  end
end

最初にオリジナルの Kernel#require_relative を実行して、見つからなかったときだけ JS::RequireRemote#load を実行します。

自分でビルドしたruby.wasmを実行する環境をつくる

ブラウザからruby.wasmを実行するときは https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@2.1.0/dist/browser.script.iife.js を使うことが多いです。 browser.script.iife.jshttps://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@2.1.0/dist/ruby+stdlib.wasmをダウンロードして実行します。 便利なスクリプトです。 ただ、自分がビルドしたruby.wasmを動かすには向きません。

そこで browser.script.iife.js をビルドします。 ruby.wasmを取得するコードは https://github.com/ruby/ruby.wasm/blob/b37b90ca139a3c1c1e710faa2f9072ecb750a0b2/packages/npm-packages/ruby-wasm-wasi/src/browser.script.ts#L7-L9 にあります。

  const response = fetch(
    `https://cdn.jsdelivr.net/npm/${pkg.name}@${pkg.version}/dist/ruby+stdlib.wasm`,
  );

任意のパスに書き換えます。 例えば、browser.script.iife.js と同じディレクトリに ruby.wasm を置く場合は、次のように修正します。

  const response = fetch(
    `ruby+stdlib.wasm`,
  );

browser.script.iife.jsをビルドします。

rake npm:ruby-head-wasm-wasi

ruby.wasm自体のビルドが終わっていれば、npmパッケージのビルドだけが実行されます。 30秒くらいで終わります。 packages/npm-packages/ruby-head-wasm-wasi/dist/browser.script.iife.jsruby+stdlib.wasmが出来ます。*1

ledsun@MSI:~/ruby.wasm►ls -la packages/npm-packages/ruby-head-wasm-wasi/dist/
total 104940
drwxr-xr-x 4 ledsun ledsun     4096 Jun 22 23:28 ./
drwxr-xr-x 5 ledsun ledsun     4096 Nov  5 11:23 ../
-rw-r--r-- 1 ledsun ledsun     1067 Nov 14 15:58 LICENSE
-rw-r--r-- 1 ledsun ledsun    51157 Nov 14 15:58 NOTICE
drwxr-xr-x 2 ledsun ledsun     4096 Jun 22 23:28 RubyRunner/
drwxr-xr-x 2 ledsun ledsun     4096 Jun 22 23:28 bindgen/
-rw-r--r-- 1 ledsun ledsun   560499 Nov 14 15:58 browser.cjs.js
-rw-r--r-- 1 ledsun ledsun      267 Nov 14 15:58 browser.d.ts
-rw-r--r-- 1 ledsun ledsun   560301 Nov 14 15:58 browser.esm.js
-rw-r--r-- 1 ledsun ledsun   566241 Nov 14 15:58 browser.script.cjs.js
-rw-r--r-- 1 ledsun ledsun       95 Nov 14 15:58 browser.script.d.ts
-rw-r--r-- 1 ledsun ledsun   566045 Nov 14 15:58 browser.script.esm.js
-rw-r--r-- 1 ledsun ledsun   623166 Nov 14 15:58 browser.script.iife.js
-rw-r--r-- 1 ledsun ledsun   578943 Nov 14 15:58 browser.script.umd.js
-rw-r--r-- 1 ledsun ledsun   566825 Nov 14 15:58 browser.umd.js
-rw-r--r-- 1 ledsun ledsun    46153 Nov 14 15:58 index.cjs.js
-rw-r--r-- 1 ledsun ledsun     6310 Nov 14 15:58 index.d.ts
-rw-r--r-- 1 ledsun ledsun    46083 Nov 14 15:58 index.esm.js
-rw-r--r-- 1 ledsun ledsun    48856 Nov 14 15:58 index.umd.js
-rw-r--r-- 1 ledsun ledsun    46923 Nov 14 15:58 node.cjs.js
-rw-r--r-- 1 ledsun ledsun      248 Nov 14 15:58 node.d.ts
-rw-r--r-- 1 ledsun ledsun    46887 Nov 14 15:58 node.esm.js
-rw-r--r-- 1 ledsun ledsun 34240164 Nov 14 15:58 ruby+stdlib.wasm
-rw-r--r-- 1 ledsun ledsun 53327741 Nov 14 15:58 ruby.debug+stdlib.wasm
-rw-r--r-- 1 ledsun ledsun 15505037 Nov 14 15:58 ruby.wasm

browser.script.iife.jsruby+stdlib.wasmにくわえて、次の index.html を用意します。

<html>
  <script src="browser.script.iife.js"></script>
  <script type="text/ruby">
    puts "Hello, world!"
  </script>
</html>

動かしてみましょう。 上の三つのファイルを置いたたディレクトリでHTTPサーバーを起動します。

ruby -run -e httpd .  

ブラウザで http://localhost:8080/ を開き、開発コンソールを開きます。 次のように Hello, world! が表示されていれば成功です。

ブラウザの開発コンソールにHello, world!が表示されているスクリーンショット

*1: 似た名前のディレクトリにpackages/npm-packages/ruby-wasm-wasi/dist/があります。ここには browser.script.iife.js は含まれません。

ruby.wasmのE2Eテストをデバッグする その2

ruby.wasmのE2Eテストをデバッグする - @ledsun blog で、次のように書きました。

そこで、テストケースで次のようなイベントリスナーを設定します。

page.on("console", (msg) => console.log("LOG:", msg.text()));

ruby.wasmのソースコードをみると元々組み込まれていました。 https://github.com/ruby/ruby.wasm/blob/c5052f289be1d331e340d77f8029fb7f3bc3be24/packages/npm-packages/ruby-wasm-wasi/test-e2e/support.ts#L9-L17

  if (process.env.DEBUG) {
    context.on("request", (request) =>
      console.log(">>", request.method(), request.url()),
    );
    context.on("response", (response) =>
      console.log("<<", response.status(), response.url()),
    );
    context.on("console", (msg) => console.log("LOG:", msg.text()));
  }

環境変数 DEBUG を設定すれば、そのまま使えそうです。 やってみましょう。

つぎのように、環境変数を設定してテストを実行します。

DEBUG=true rake npm:ruby-head-wasm-wasi:check

デバッグログがテスト実行画面に表示されているスクリーンショット

出てきました。 レスポンスステータスや、モックされたURLも表示されて便利です。

よく見ると、不思議な出力があります。

<< 200 https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/redirect_to_error_on_load_twice.rb

テスト用のプロキシサーバーに、次のような301 レスポンスを返しているつもりです。

    } else if (relativePath.match("redirect_to_error_on_load_twice.rb")) {
      route.fulfill({
        status: 301,
        headers: {
          location: "error_on_load_twice.rb",
        },
      });
   }

ログでは、200と表示されています。 リダイレクトが上手く行っていないのでしょうか?

つづくリクエストはリダイレクト先に飛んでいます。 リダイレクトに従っているように見えます。

>> GET https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@latest/dist/error_on_load_twice.rb

謎です。

Response.urlの挙動を確認する

ruby.wasmのE2Eテストをデバッグする - @ledsun blog で、Response.urlにリダイレクト後のURLが入っていない現象を観測しました。

MDNには

url プロパティの値は、あらゆるリダイレクトの後に得られる最終的な URL になります。

とあります。矛盾しているように思えます。 問題への理解を深めるために、よりシンプルな環境でRespose.urlの動作を確認します。

次のようにリダイレクトレスポンスを返すサーバーを作成します。

require 'webrick'

server = WEBrick::HTTPServer.new(Port: 3000)

# リダイレクトの設定
server.mount_proc '/redirect' do |req, res|
  res.status = 302
  res['Location'] = 'anywhere'
end

trap 'INT' do
  server.shutdown
end

server.start

起動します。

ruby server.rb

ブラウザで http://localhost:3000 を開きます。

ブラウザで開いて404ページが表示されているスクリーンショット

404ページが表示されます。 今回はHTMLの内容には興味がありません。 これで十分です。

開発コンソールを開きます。 次のようにfetch APIを使って、redirectするURLにリクエストを送り、レスポンスを得ます。

response = await fetch('redirect')

開発コンソールでResposeが表示されているスクリーンショット

Response.url は "http://localhost:3000/anywhere" です。 リダイレクト後のURLが入っています。 前回観測した現象と異なります。 MDNの記述と同じ動作をします。 PlayWrightの実行環境が特殊なのでしょうか? 謎です。

ruby.wasmのE2Eテストをデバッグする

ruby.wasmにPlayWrightで書かれたE2Eテストがあります。 https://github.com/ruby/ruby.wasm/blob/84707d656d007577947ced6c14a9a33e3ffd4033/packages/npm-packages/ruby-wasm-wasi/test-e2e/integrations/browser-script.spec.ts

テストに失敗したときに、次のようにタイムアウトエラーだけが表示されることがあります。

タイムアウトエラーのスクリーンショット

ロジック途中でエラーが起き、アサーションまで到達しないときによく起きます。 この情報から失敗の原因を突き止めるのは大変です。

PlayWrightではブラウザを起動し、その中でruby.wasmを実行しています。 JavaScriptRubyスクリプトデバッグプリントしても、ブラウザのコンソールに出力されます。 テストを実行しているコンソールには表示されません。

そこで、テストケースで次のようなイベントリスナーを設定します。

page.on("console", (msg) => console.log("LOG:", msg.text()));

実行しているページでコンソールへの出力があったら、テストの実行画面に出力します。 次のようにテスト実行画面でデバッグプリントの内容が確認できます。

テスト実行画面にデバッグプリントが表示されているスクリーンショット

この情報から、僕はResponse.urlにはリダイレクト後のURLが入っていることを期待していたのに、リダイレクト前のURLがとれたので上手く動いていないことがわかりました。

透過pngを作る 2

透過pngを作る - @ledsun blog の続きです。 手元にあるツールで、背景色の透明化を試しましたが上手く行きませんでした。 新しツールをインストールしてみます。

フリーの画像編集ソフトといえばGIMPがあります。 最近はMicrosoft Storeからインストール出来るようです。 GIMP 2.10.34 on Microsoft Store

GIMPには「ファジー選択ツール」が有ります。 「ファジー選択ツール」を使って背景を選択して削除します。

ファジー選択ツールを使って背景を削除

簡単でした。 pngとして保存するには「名前を付けてエクスポート」します。

GIMPで背景を透明化した画像

緑の代わりに白が少し残ります。ファジー選択のしきい値を調整する必要がありそうです。 次の値でやってみます。

しきい値を調整して透明化した画像

綺麗に抜けました。 ただ、装飾も消えました。 もうちょっと調整が必要そうです。

まあ、でも装飾ない方がすっきりしてていいかもしれません。 怪我の功名です。