ottijp blog

Share(Action) エクステンションのJavaScriptをテストする

2021-06-20iOSJavaScripttestingseleniumsafarinode

Share(Action) エクステンションに対応したiOSアプリを作る際,Safariに対し特定のJavaScriptファイルの実行(と結果の受け取り)をさせることができます.

参考: App Extension Programming Guide: Handling Common Scenarios の”Accessing a Webpage”

このJavaScriptのテストをしたかったので,実現方法を考えてみました.

環境

  • macOS: 10.15 (Catalina)
  • Node.js: v10.18
  • Safari: 14.1

方法

本当はiOS上でテストできればよいのですが,テストフレームワークからiOSのSafariを制御したりするのが難しそうだった(わからなかった)ので,せめて似た環境になるように,Seleniumを使ってmacOS上のSafariでテストしました. テストの実行は次のようなことをしています.

  • serveでWebサーバを立てる
  • SafariをSeleniumで動かし,立てたWebサーバにアクセスする
  • テストするJavaScriptをSafariに読み込ませる
  • テストメソッドでアサーションを実行する

この記事ではrunだけテストしていますが,同様にfinalizeを呼ぶことでfinalizeのテストもできると思います.

実装

テスト対象のJavaScriptはAppleのガイドを参考に,次のようなものを作りました. ベースURLとページタイトルを取得するものです. これがiOSアプリのプロダクトコードの一部であり,エクステンション実行時に実際に呼ばれることになります.

src/processing.js
var MyExtensionJavaScriptClass = function() {};

MyExtensionJavaScriptClass.prototype = {
  run: function(arguments) {
    arguments.completionFunction({
      "baseURI": document.baseURI,
      "title": document.title
    });
  },

  finalize: function(arguments) {
  }
};

テスト用Webサーバの起動はこんな感じです.Safariの立ち上がりが遅いことがあったので,タイムアウトを長めにしています. また,テスト終了時にすぐにサーバ停止するように,keepAliveTimeoutは短めに設定しています.

before(async function() {
  this.timeout(10000)

  // Webサーバの起動
  server = http.createServer((request, response) => {
    return handler(request, response, {
      public: path.join(__dirname, '..', 'public'),
      directoryListing: false,
    })
  })
  server.keepAliveTimeout = 500
  server.listen(3000)

  // Safariのドライバ
  driver = await new Builder().forBrowser('safari').build()
  // テスト対象のJavasScript
  processingJs = await util.promisify(fs.readFile)(processingJsPath, 'utf-8')
})

テスト終了時はこのようにWebサーバを停止します.

after(async function() {
  // Webサーバの停止
  await driver.quit()
  server.close()
})

テスト対象のJavaScriptはこんな感じでSafariに実行させています.

async function runProcessingJs() {
  return await driver.executeScript(async (processingJs) => {
    eval(processingJs)
    return new Promise((resolve) => {
      ExtensionPreprocessingJS.run({
        completionFunction: (runResult) => {
          resolve(runResult)
        }})
    })
  }, processingJs)
}

テストメソッドはこんな感じです.ここでもSafariの動作が遅いことがあったので,タイムアウトを長めに設定しています.

describe('processing', function() {
  this.timeout(5000)

  // 日本語ページのベースURLとタイトルを取得する
  it('ja', async function() {
    await driver.get('http://localhost:3000/ja')
    const result = await runProcessingJs()
    console.log('結果', result)
    assert(result.title === '日本語のページ<アングルブラケット>')
    assert(result.baseURI === 'http://localhost:3000/ja')
  })
})

すべてのファイルはottijp/test-extension-javascriptにコミットしてあるので参照してください.

テスト実行

テストを実行するとこのような出力になります.

$ npm test

> test-extension-javascript@1.0.0 test /Users/otti/src/github.com/ottijp/test-extension-javascript
> mocha



  processing
    ✓ ja (242ms)


  1 passing (2s)

ottijp
Satoshi SAKAO (@ottijp)

都内でアプリケーションエンジニアをしています

...