PCのDNSキャッシュをクリアする方法

Webサイトの引っ越しなどで、DNSレコード設定を編集しました。こちらのPCでは新サイトを表示できました。しかし、隣のPCでは旧サイトのままで、なかなか新サイトを表示しないことがあります。そのPCのDNSキャッシュに、旧サイトの情報が残っているのかもしれません。

簡単な方法は、そのPCを再起動することです。PCを再起動するとDNSキャッシュがクリアされて、新サイトを表示できるようになります。ところが、再起動に時間がかかる、別のアプリケーションを開いているなど、なるべく再起動したくない場合があります。

ここでは、PCを再起動することなく、DNSキャッシュをクリアする方法を説明します。

Windows

コマンドプロンプトを開いて、次のコマンドを実行してください。

:DNSキャッシュをクリアする
C:\Users\taro>ipconfig /flushdns

:DNSキャッシュを表示する
C:\Users\taro>ipconfig /displaydns

macos

OS X で DNS キャッシュをリセットする
https://support.apple.com/ja-jp/HT202516

OS X v10.10.4 以降では、以下のターミナルコマンドを実行してください。

$ sudo killall -HUP mDNSResponder

Ubuntu

ターミナルを開いて、次のコマンドを実行してください。

# DNSキャッシュ状況を表示する
$ systemd-resolve --statistic

# DNSキャッシュをクリアする
$ sudo systemd-resolve --flush-caches

# network-managerを再起動 -> DNSレゾルバも再起動 -> DNSキャッシュクリア
$ sudo sudo systemctl restart network-manager.service

参考: dnsmasqのキャッシュ状況

$ sudo pkill -USR1 dnsmasq
$ sudo tail /var/log/syslog

Google Chrome

上記の方法でDNSキャッシュをクリアした後、さらにGoogle chromeのDNSキャッシュをクリアする必要があります。
chromeのアドレスバーに chrome://net-internals/#dns を入力してください。
「Host resolver cache [Clear host cache]」と表示されます。
[Clear host cache]ボタンをクリックしてください。

自炊pdfの余白を削除するツール scanned-pdf-margin-cutter

npm
https://www.npmjs.com/package/scanned-pdf-margin-cutter
github
https://github.com/ninton/scanned-pdf-margin-cutter 使い方 まず、本のカバーだけをカラーでスキャンして、pdfにします。
本体をカラーまたはグレースケールでスキャンして、pdfにします。 本体について、次の4つを定規で測ります。
1.上側から、切りつめたい長さ(ミリ)
2. 綴じていない側から、切りつめたい長さ(ミリ)
3. 残したい領域の幅(ミリ)
4. 残したい領域の高さ(ミリ) 左綴じで、幅160mm、高さ200mmの本とします。
上側から切りつめたい長さ=10mm
右側(綴じていない側)から、切りつめたい長さ=10mm
残したい領域の幅=130mm
残したい領域の高さ=170mm 整理すると、
左から、20mmカット、130mm残し、10mmカット、です。
上から、10mmカット、170mm残し、20mmカット、です。 すべてのページをこの数値で余白カットしていいでしょうか?
上下方向は問題ないのですが、左右方向は問題があります。 1ページ目は、
左から、20mmカット、130mm残し、10mmカット、です。
2ページ目は、左右を逆転して、
左から、10mmカット、130mm残し、20mmカット、です、
なぜなら、2ぺーじ目は、1ページ目の裏側に印刷されているからです。 つまり、左綴じの場合は、
奇数ページは、左から、20mmカット、130mm残し、10mmカット、です。
偶数ページは、左から、10mmカット、130mm残し、20mmカット、です。 このツールは、本体pdfは、左綴じ、右綴じ、奇数ページ、偶数ページを考慮して、左右余白を計算してカットします。 表紙pdfは、余白カットはせず、(残したい領域の幅)×(残したい領域の高さ)に縮小します。 最終的に、新規pdf(yyyy-mm-dd-HH-MM-SS.pdf)に結果を保存します。表紙pdfや本体pdfは変更しないので、余白の数値を変えながら、何回でも試すことができます。 重要な処理は、GhostScript、ImageMagick、PDFtkが処理しています。GhostScriptが、表紙pdfや本体pdfからページ画像を取り出します。ImageMagickが、ページ画像の余白カットや縮小をして、1画像ごとに1つのpdfに変換します。最後にPDFtkが、全ページpdfを1ファイルに結合します。

コマンドラインで自炊したPDFのページを並び替える

自炊した本を読んでいたら、ページの並びが間違っていることに気づきました。1〜160ページは正常、161〜270ページが逆順、271〜364ページは正常。つまり、160ページまで読み、270ページへ飛び、戻りながら161ページへ、そして271ページへ飛び、という順序の本になっていました。

実物の本は廃棄してしまったので、再スキャンはできません。そこで、PDFファイルのページ順序を並び替えることにしました。

pdftk

https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/
pdftkをインストールします。(筆者の実行環境はUbuntu16)
$ sudo apt install pdftk
(1) まず、input.pdfファイルを1〜160ページ、161〜270ページ、271〜364ページの3つに分解します。
$ pdftk input.pdf cat 1-160 output part1.pdf
$ pdftk input.pdf cat 161-270 output part2.pdf
$ pdftk input.pdf cat 271-364 output part3.pdf
(2) part2.pdfを1ページ1pdfに分解します。
# part2_old_split.sh
mkdir part2
for ((i=1; i <= 110; i +=1)); do
pdftk part2.pdf cat $i output part2/$i.pdf
done
$ bash part2_old_split.sh
(3) 各ページのpdfを希望の順序で指定して、part2_new.pdfを作ります。
# part2_new_join.sh
echo "pdftk \\"
for ((i=110; i >= 1; i -=1)); do
echo "part2/$i.pdf \\"
done
echo cat output part2_new.pdf
$ bash part2_new_join.sh | bash
(4) part1.pdf、part2_new.pdf、part3.pdfを結合して、output.pdfを作成します。
$ pdftk \
part1.pdf \
part2_new.pdf \
part3.pdf \
cat output output.pdf
(5) 必要に応じて、ScanSnapオーガナイザーで、OCR処理して、検索可能なテキストを追加します。

ボツになった方法 ImageMagick convert

まず、convertコマンドpdfファイルをjpg画像に分解します。(自炊したPDFなので、1ページ1画像)
$ convert input.pdf page.jpg
次に、convertコマンドで、jpg画像を希望の順序に指定して、pdfに結合します。
$ convert \
page-0.jpg \
page-1.jpg \
省略
page-159.jpg \
page-269.jpg \
省略
page-160.jpg \
page-270.jpg \
省略
page-363.jpg \
output.pdf
ページ数が多すぎたようで、処理が進みません。システムモニターを見ると、CPUの1コアが100%、32Gのメモリも使いきり、システムモニターの表示もカクカクしていました。コントロールCで中止しました。

ubuntu16.04でAndroid StudioのEmulatorが起動しない

環境
ubuntu 16.04
Android studio 3.0.1

エミュレータを起動しようとすると、起動せずに終了してしまいます。

Event Log

17:49   Emulator: libGL error: unable to load driver: nouveau_dri.so

17:49   Emulator: libGL error: driver pointer missing

17:49   Emulator: libGL error: failed to load driver: nouveau

17:49   Emulator: libGL error: unable to load driver: swrast_dri.so

17:49   Emulator: libGL error: failed to load driver: swrast

17:49   Emulator: X Error of failed request:  BadValue (integer parameter out of range for operation)

17:49   Emulator: Major opcode of failed request:  155 (GLX)

17:49   Emulator: Minor opcode of failed request:  24 (X_GLXCreateNewContext)

17:49   Emulator: Value in failed request:  0x0

17:49   Emulator: Serial number of failed request:  58

17:49   Emulator: Current serial number in output stream:  59

17:49   Emulator: Process finished with exit code 1

調べた記事を参考に、ANDROID_EMULATOR_USE_SYSTEM_LIBS=1を設定してみました。

export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1

Googleロゴまで表示されるようになりましたが、直後に終了してしまいます。

Event Log

17:51   Emulator: Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

「ubuntu Emulator: Process finished with exit code 139」で検索すると、次の記事が見つかりました。

ubuntuでAndroid StudioのEmulatorが起動しない問題
https://qiita.com/yuqengo/items/ffd7f916c276bd4f84ce

エミュレータ設定のGraphicsをAutomaticからSoftwareに変更したら、起動しました。

「export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1」を設定しなくても、起動しました。

gitブランチの名前の付け方2

以前、gitブランチの名前の付け方について紹介したことがあります。

gitのブランチの名前の付け方

実際に定着している名前は、次の4つです。
プロジェクトによっては、master、developだけで済ましてしまうこともあります。

(1) master
製品ブランチ

(2) develop
リリース前の確認用ブランチ

(3) personals/aoki/develop
開発者ごとの作業ブランチです。developからブランチします。

(4) personals/aoki/1
一時的な作業ブランチです。最後の数字はチケット番号です。personals/aoki/developですでに作業中のとき、別チケットの作業をしたいときの一時的なブランチです。developからブランチします。コミットして、developへマージしたら、すぐに削除します。

今は使わなくなった方法も紹介しましょう。

(ボツ1) ブランチ名に機能の名称をつける

チケットにする前の実験的な機能を試したいときに、ブランチ名に機能の名称をつけたことがあります。

例えば、
develop_camera
develop_news

コレという名前を考えるのが面倒でした。チケットを起こしてチケット番号をつけたほうが楽です。そもそも実験的なので放置することも多く、何をやっていたかを覚えていないこともあります。チケットにアイデアをメモしておくと、何をやっていたか思い出しやすくなります。

(ボツ2) 複数の作業ブランチ

gitは簡単にブランチ作成できることもあって、
personals/aoki/1
personals/aoki/2
personals/aoki/3
といったように、複数の作業ブランチを作成していたことがあります。

confictの解決も手間ですが、conflictがなかったとしても、マージ操作自体が面倒でした。

例えば、personals/aoki/1の作業が一段落したら、
personals/aoki/1からdevelopへマージ
developからpersonals/aoki/2へマージ
developからpersonals/aoki/3へマージ
する必要があります。

さらに、他の開発者がdevelopへpushしたら、
developからpersonals/aoki/1へマージ
developからpersonals/aoki/2へマージ
developからpersonals/aoki/3へマージ
する必要があります。

このようにブランチ切り替えとマージ操作が多くて面倒だったので、ブランチを作りすぎるのもどうかと感じました。

「継続的デリバリー」という本では、複数人で開発していても、メインブランチ1本を推奨しています。ブランチ間のマージが面倒になるため、またメイン以外のブランチは自動テストしないことが多いためです。また、少なくても1日に1回、たいていは1日に数回コミットすることも推奨しています。

android F-03G NFCの項目がグレー表示でチェックを付けることができない

FujitsuのタブレットF-03Gを2台持っています。

F-03GでNFCを使うには、設定>便利機能>NFCにチェックを付けて、NFCを有効にします。

1台のF-03Gは問題なくNFCを使うことができました。

ところが、もう1台のF-03Gは、設定>便利機能>NFCの項目がグレー表示になっていて、チェックを付けることができません。

なぜ?と思って調べたところ、USB充電しているとグレー表示になるようです。グレー表示のF-03Gは、USB充電ケーブルをつないだままでした。USB充電ケーブルをはずしたら、チェックを付けることができ、NFCを読み込むことができました。

参考URL
価格.com – 『NFCがONにできない』富士通 ARROWS Tab F-02F docomo
http://bbs.kakaku.com/bbs/K0000584870/SortID=17118914/

デバッガでトレースしやすい書き方

IDEのデバッガには、1行づつの「ステップ」、関数の中へ入る「ステップイン」、関数から戻る「ステップオーバー」、ローカル変数やメンバー変数の値を表示する機能があります。式の一部分を指定して評価する機能もありますが、その操作をする必要があります。

「ステップイン」することなく、「ステップ」だけでどこを通っているかを確認できたり、関数の戻り値を確認できると、デバッグしやすいです。

以下の例は、架空の言語です。

1行に関数を2つ以上書くと、2つめ以降の関数にステップインしにくい

デバッグしにくい例
func_b内にステップインするためには、1回目の「ステップイン」でfunc_aにステップインします。「ステップオーバー」でこの行に戻ります。2回目の「ステップイン」でfunc_bにステップインします。

func_a(); func_b();

デバッグしやすい例
func_bの行で止まり「ステップイン」すると、func_b内にステップインします。

func_a();
func_b();

if文の条件部分と本体を1行にすると、ifの本体を通ったのかわからない

デバッグしにくい例
ステップ実行すると、if文のtrue/falseに関係なく、次の行に進みます。funcを呼んだかどうかがわかりづらいです。

if (1 < a) { func(); }

デバッグしやすい例
ステップ実行すると、if文がtrueなら、func()の行で止まります。

if (1 < a) {
    func(x);
}

if文の条件式が複雑だと、デバッグしにくい

デバッグしにくい例

if (x <= func_a() && y <= func_b()) {
}

デバッグしやすい例

function is_xxxx(x, y) {
    a = func_a();
    b = func_b();
    f1 = x <= a;
    f2 = y <= b;
    f = f1 && f2;
    return f;
}

if (is_xxxx(x, y)) {
}

関数Aの引数で関数Bを呼ぶと、関数Bの戻り値を確認しにくい

デバッグしにくい例
func_bの戻り値を確認するには、func_aにステップインする必要があります。
また「1行に関数を2つ以上書くと、2つめ以降の関数にステップインしにくい」と同じ問題があります。

func_a(func_b());

デバッグしやすい例
func_a(b);の行で止まると、変数のbの値が表示されます。

b = func_b();
func_a(b);

return式で関数Aを呼ぶと、関数Aの戻り値を確認しにくい

デバッグしにくい例
funcの戻り値を確認できません。

return func();

デバッグしやすい例
return result;で止まると、変数resultの値が表示されます。

result = func();
return result;

右辺の式内で関数を呼ぶと、戻り値を確認しにくい

デバッグしにくい例
func_aやfunc_bにステップインしないと、戻り値を確認できません。

x = func_a() + func_b();

デバッグしやすい例
x = a + b;の行で止まると、変数aや変数bの値が表示されます。

a = func_a();
b = func_b();
x = a + b;

判断と処理を分けて読みやすくする

判断と処理を分けると、読みやすく、テストしやすく、デバッグしやすくなります。

例は javascriptで書いています。

1.初期バージョン

最初に書くバージョンはだいたいこんな感じです。
「もろもろの処理」は、複数行だったり、if文やfor文を含むことがあります。

function func2 (a, b) {
    if (a > 10) {
        if (a > 20) {
            if (a > 30){
                もろもろの処理_1
            } else {
                もろもろの処理_2
            }
        } else {
            もろもろの処理_3
        }
    } else {
        もろもろの処理_4
    }
}

2.もろもろの処理をそれぞれ別メソッドにします。

function func2 (a, b) {
    if (a > 10) {
        if (a > 20) {
            if (a > 30){
                func2_1();
            } else {
                func2_2();
            }
        } else {
            func2_3();
        }
    } else {
        func2_4();
    }
}

function func2_1() {
    もろもろの処理_1
}

function func2_2() {
    もろもろの処理_2
}

function func2_3() {
    もろもろの処理_3
}

function func2_4() {
    もろもろの処理_4
}

3.「判断」と「処理」を別メソッドにします。

function func2 (a, b) {
    var num = case_number(a, b);
    process(num);
}

function case_number(a, b) {
    var num = 0;
    if (a > 10) {
        if (a > 20) {
            if (a > 30){
                num = 1;
            } else {
                num = 2;
            }
        } else {
            num = 3;
        }
    } else {
        num = 4;
    }
    return num;
}

function process(num) {
    switch (num) {
        case 1:
            func2_1();
            break;
        case 2:
            func2_2();
            break;
        case 3:
            func2_3();
            break;
        case 4:
        default:
            func2_4();
            break;
    }
}

4.判断内のif文のネストをelse〜ifで小さくします。

function case_number(a, b) {
    var num = 0;

    if (a <= 10) {
        num = 4;
    } else if (a <= 20) {
        num = 3;
    } else if (a <= 30) {
        num = 2;
    } else {
        num = 1;
    }

    return num;
}

5.デシジョンテーブル風のコメントをつけておきます。

/*
| # | a     | action  |
| - | ----- | ------- |
| 4 | <= 10 | func2_1 |
| 3 | <= 20 | func2_2 |
| 2 | <= 30 | func2_3 |
| 1 | > 30  | func2_4 |
*/
function func2 (a, b) {
   ...
}

6. if文の条件式が長いときは、それぞれ別メソッドにします。

function case_number(a, b) {
    var num = 0;

    if (is_later_equal(a, 10)) {
        num = 4;
    } else if (is_later_equal(a, 20)) {
        num = 3;
    } else if (is_later_equal(a, 30)) {
        num = 2;
    } else {
        num = 1;
    }

    return num;
}

function is_later_equal(a, x) {
    var f = (a <= x);
    return f;
}

最後に、func2メソッドの処理をとりだして、別クラスにします。

cordovaで開発したiPhoneアプリをappiumでテスト(2/2)

cordovaで開発したiPhoneアプリをAppiumでテストしたので、まとめました。

cordovaで開発したiPhoneアプリをappiumでテスト(1/2)
1.目標
2.simulator用appをビルド
3.appium他のインストール
4.Appiumデスクトップのインストール
5.Appiumデクトップを起動、Inspectorウィンドウで操作の記録

cordovaで開発したiPhoneアプリをappiumでテスト(2/2)
6.nodeでテストケース作成(アプリ起動のみ)
7.テストケースの実行
8.テストケース作成(座標をタップ、スクリーンショット保存)

6.nodeでテストケース作成

githubにappium公式サンプルソースがあります。
https://github.com/appium/sample-code

sample-code/examples/node/helpers/*.js
sample-code/examples/node/ios-simple.js
sample-code/examples/node/package.json
をダウンロードして、ios-simple.jsを改造します。

/Users/taro/projects/hello/platforms/ios/appium-testcaseの下に
helpers/*.js
ios-simple.js
package.json
を保存しました。

必要なnodeモジュールをインストールします。

$ cd /Users/taro/projects/hello/platforms/ios/appium-testcase
$ npm install

beforeメソッドのdesired変数は、「4.Appiumデクトップを起動、Inspectorウィンドウで操作の記録」のdesired capbilitiesの各項目を参考に設定してください。

テストケースの内容は、アプリを起動して、1秒間表示して、終了するだけです。

ios-simple.jsを編集します。

"use strict";

require("./helpers/setup");

var wd = require("wd"),
    _ = require('underscore'),
    Q = require('q'),
    serverConfigs = require('./helpers/appium-servers');

describe("ios simple", function () {
  this.timeout(300000);
  var driver;
  var allPassed = true;

  before(function () {
    var serverConfig = process.env.npm_package_config_sauce ?
      serverConfigs.sauce : serverConfigs.local;
    driver = wd.promiseChainRemote(serverConfig);
    require("./helpers/logging").configure(driver);

    var desired = {
      "appium-version": "1.4",
      platformName: "iOS",
      deviceName: "iPhone 7",
      platformVersion: "11.0",
      automationName: "XCUITest",
      app: "/Users/taro/projects/hello/app/Applications/HelloWorld.app",
    }; 

    return driver.init(desired);
  });

  after(function () {
    return driver
      .quit()
      .finally(function () {
        if (process.env.npm_package_config_sauce) {
          return driver.sauceJobStatus(allPassed);
        }
      });
  });

  afterEach(function () {
    allPassed = allPassed && this.currentTest.state === 'passed';
  });

  it ("test_1", function () {
    return driver
      .sleep(1000)
  });

});

7.テストケースの実行

appiumサーバの起動

Appiumデスクトップで[Start Server]しておくか、または、ターミナルで

appiumサーバの起動
$ appium &

appiumサーバの停止
$ pkill -f appium

テストケースの実行

$ cd /Users/taro/projects/hello/platforms/ios/appium-testcase
$ npm run ios-simple

iPhone simulatorの終了

$ pkill -f CoreSimulator

8.テストケース作成(座標をタップ、スクリーンショット保存)

Inspectorウィンドウで、xy座標をメモしてはクリックを繰り返して、テストケースを作りました。
次のように、座標指定のタップ、スクリーンショット保存処理、AlertダイアログのOKを追加しました。

"use strict";

require("./helpers/setup");

var wd = require("wd"),
    _ = require('underscore'),
    Q = require('q'),
    serverConfigs = require('./helpers/appium-servers'),
    fs = require('fs');

describe("ios simple", function () {
  this.timeout(300000);
  var driver;
  var allPassed = true;

  before(function () {
    var serverConfig = process.env.npm_package_config_sauce ?
      serverConfigs.sauce : serverConfigs.local;
    driver = wd.promiseChainRemote(serverConfig);
    //require("./helpers/logging").configure(driver);

    var desired = {
      "appium-version": "1.4",
      platformName: "iOS",
      deviceName: "iPhone 7",
      platformVersion: "11.0",
      automationName: "XCUITest",
      app: "/Users/taro/projects/hello/app/Applications/HelloWorld.app",
    }; 

    driver.saveScreenshot = function (path) {
      return driver.takeScreenshot().then(function(data) {
        fs.writeFile(path, data, "base64", function(err) {
          if (err) {
           throw err;
          }
        });
      });
    };

    return driver.init(desired);
  });

  after(function () {
    return driver
      .quit()
      .finally(function () {
        if (process.env.npm_package_config_sauce) {
          return driver.sauceJobStatus(allPassed);
        }
      });
  });

  afterEach(function () {
    allPassed = allPassed && this.currentTest.state === 'passed';
  });

  var tap_at = function (x, y) {
    var action = new wd.TouchAction();
    return action
      .press({"x": x, "y": y})
      .wait(100)
      .release();
  }

  it ("test_1", function () {
    return driver
      .sleep(1000)
      .saveScreenshot("/Users/taro/projects/hello/platforms/ios/appium-testcase/screenshots/01.png")

      .performTouchAction(tap_at(161, 464))
      .sleep(1000)
      .saveScreenshot("/Users/taro/projects/hello/platforms/ios/appium-testcase/screenshots/02.png")

      .acceptAlert()
      .sleep(1000)
      .saveScreenshot("/Users/taro/projects/hello/platforms/ios/appium-testcase/screenshots/03.png")
  });

});

最終的に、desired変数のdeviceName、platformVersion、appやスクリーンショット保存パスは、環境変数で渡すようにしました。
また、シミュレータでの実行が思ったより遅かったので、sleepで調整しています。

cordovaで開発したiPhoneアプリをappiumでテスト(1/2)

cordovaで開発したiPhoneアプリをAppiumでテストしたので、まとめました。

cordovaで開発したiPhoneアプリをappiumでテスト(1/2)
1.目標
2.simulator用appをビルド
3.appium他のインストール
4.Appiumデスクトップのインストール
5.Appiumデクトップを起動、Inspectorウィンドウで操作の記録

cordovaで開発したiPhoneアプリをappiumでテスト(2/2)
6.nodeでテストケース作成(アプリ起動のみ)
7.テストケースの実行
8.テストケース作成(座標をタップ、スクリーンショット保存)

当初、XcodeのXCUITestでwebviewのDOMを取得できなかったので、appiumを使ってみました。結局、appiumでもwebviewのDOMを取得できず、xy座標を指定してタップ操作を記述することにしました。それなら、XCUITestでテストケースを書いたほうがよかったかもしれません。

1.目標

全画面を遷移し、スクリーンショットを保存する。

対象機種のシミュレータ:
iPhone X、iPhone 8、iPhone 7 (11.0)、iPhone 7 Plus (11.0)、iPhone 6 (10.3)

2.simulator用appをビルド

cordovaプロジェクトの位置=/Users/taro/projects/hello
xcodeワークスペースの位置=/Users/taro/projects/hello/platforms/ios/HelloWorld.xcworkspace
appのファイル位置=/Users/taro/projects/hello/app/Applications/HelloWorld.app

ターミナルで次のsim_build.shを実行します。
app/Applications/HelloWorld.appが作成されます。

$ cd /Users/taro/projects/hello
$ ./sim_build.sh

/Users/taro/projects/hello/sim_build.sh

#!/bin/bash -x

WORKSPACE=HelloWorld
SCHEME=HelloWorld
CONFIG=Debug
SDK=iphonesimulator

xcodebuild \
  -workspace $WORKSPACE \
  -scheme $SCHEME \
  -configuration $CONFIG \
  clean \
  DSTROOT=${PWD}/app

xcodebuild \
  -workspace $WORKSPACE \
  -scheme $SCHEME \
  -configuration $CONFIG \
  -sdk iphonesimulator \
  install \
  DSTROOT=${PWD}/app

3.appium他のインストール

必要なものをインストールしてください。
ターミナルの cd する場所はどこでもかまいません。

Homebrewのインストール
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

rbenvのインストール
$ brew install rbenv ruby-build readline

RubyのパスをBashに追記
$ echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
$ source ~/.bash_profile

Rubyのインストール
$ rbenv install --list
$ rbenv install 2.4.2
$ rbenv global 2.4.2
$ ruby -v

$ npm install -g appium
$ npm install -g appium-doctor
$ brew install carthage

環境確認
$ appium-doctor --ios

4.Appiumデスクトップのインストール

デスクトップ版 appium
https://github.com/appium/appium-desktop/releases

最新バージョンの appium-desktop-x.x.x.dmg をダウンロード、インストールしてください。

4.Appiumデクトップを起動、Inspectorウィンドウで操作の記録

Finder>アプリケーション>Appiumを起動してください。
[Simple]のまま、[start server]をクリックしてください。

黒いログコンソールが表示されます。
右上の虫メガネ[Start Inspector Session]ボタンをクリックしてください。

Inspectorウィンドウが表示されます。
Desired Capabilitiesタブで、次の5項目を設定してください。

項目
platformName text iOS
automationName text XCUITest
deviceName text iPhone 7
platformVersion text 11.0
app text /Users/taro/projects/hello/app/Applications/HelloWorld.app

platformName
“iOS”固定です

automationName
“XCUITest”固定です

deviceName
“iPhone 7″、”iPhone 7 Plus”、”iPhone 6″、”iPhone 6 Plus”などを入力します。

platformVersion
“10.3”、”11.0″などを入力します。
Xcodeで”9.3″シミュレータもダウンロード済みでしたが、appiumから”9.3″は起動できず諦めました。

app
「1.simulator用appをビルド」で作成したappファイルの絶対パスを指定してください。

[Start Session]をクリックしてください。
しばらくすると、iPhoneシミュレータが表示されます。
iPhoneシミュレータでテスト対象アプリが起動します。
Inspectorウィンドウにスクリーンショットが表示されます。

スクリーンショット上でマウスを動かしてみてください。
識別可能な画面要素が黄色くなり、クリックすると、緑になります。
ウィンドウの中央のApp Source領域には、ツリーが表示されます。
右側のSelected Element領域には、クリックした画面要素のXCUIElementツリーのxpathが表示されます。

cordovaで開発したアプリなので、画面要素はWebView内です。残念ながらDOM IDで画面要素を指定できないようです。画面要素が存在していること、属性が期待した値になっていること、などをアサーションしたいのですが、今回はあきらめました。

いろいろ試して、次の操作でテストケースを書くことにしました。

sleep(ミリ秒)
x, y座標指定のタップ
alertダイアログのOK
スクリーンショットの保存

iPhone 7でxy座標を調べて、iPhone 7 Plusなら座標を1.1倍することにし、iPhone Xは別に座標定義を用意することにしました。

画面ごとにスクリーンショットを保存し、担当者が見て確認することにしました。

(つづく)