RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと変更されてた

RobotFrameworkのSelenium2LibraryのGitHubを見ていたところ、名前が変更されたのに気づいたため、メモ。

 
目次

 

環境

  • Mac OS X 10.11.6
  • Python 3.6.2
  • Google Chrome 60.0.3112.113 (stable)
  • ChromeDriver 2.31 (stable)
  • RobotFramework 3.0.2
  • SeleniumLibrary 3.0.0a2

 

公式サイトの確認

GitHubを見たところ、以下のコメントがありました。

Selenium2Library 3.0 and newer extend the new SeleniumLibrary and thus contain exactly the same code and functionality. There have been lot of internal changes in the library, but external functionality provided by keywords should be fully backwards compatible. Libraries and tools using Selenium2Library internally may need to be updated to support Selenium2Library 3, though. Selenium2Library 1.8 is the latest, and last, legacy version with the old architecture and code.

Selenium2Library 3 supports Python 2.7 as well as Python 3.3 and newer. Selenium2Library 1.8 supports Python 2.6-2.7.

https://github.com/robotframework/Selenium2Library

 
そこでリンク先(https://github.com/robotframework/SeleniumLibrary)を確認したところ、たしかに今までのものが移動されていました。
https://github.com/robotframework/SeleniumLibrary

 
また、すでにdeprecatedしていた初期のライブラリについては、リポジトリ名が OldSeleniumLibrary へと変わっていました。
robotframework/OldSeleniumLibrary: Deprecated Selenium library for Robot Framework

 
PyPiを見ると、こんな感じでした。

 

影響

ライブラリ名が変わったことの影響を記載します。

pipでインストールするライブラリ名の変更

robotframework-selenium2library から robotframework-seleniumlibrary へと変わりました。

今まで

pip install robotframework-selenium2library

 
これから

現時点では移行段階のため pre などのオプションが必要ですが、将来は不要になるかと。

pip install --pre --upgrade robotframework-seleniumlibrary

 

Settingsで読み込むライブラリ名

過去のテストコードを修正しないといけないため、こちらのほうが影響が大きそうです。

今まで

Library  Selenium2Library

 
これから

Library  SeleniumLibrary

 

ドキュメントへのリンク

READMEにもありますが、ドキュメントも移動しています。

今まで

http://robotframework.org/Selenium2Library/Selenium2Library-1.8.0.html

 
これから

http://robotframework.org/SeleniumLibrary/SeleniumLibrary.html

 

自作のサンプルコード

過去に作成したサンプルコードについては、SeleniumLibrary 3.x系へとバージョンアップし、すべての動作を確認しました。
thinkAmi-sandbox/RobotFramework-sample: Robot Framewrok samples

Robot Framework + Selenium2Libraryで、リファラを書き換えるChrome拡張をWebDriverに入れてテストする

リファラによって挙動が変わるWebアプリをRobot Frameworkでテストすることがありました。

Webアプリのコードを書き換えたくないため、なにか良い方法がないかを探したところ、

を組み合わせれば良さそうでした。

その時に試した内容をメモしておきます。

 
2017/9/2追記

Selenium2Libraryですが、バージョン3からは SeleniumLibrary へと名称が変更されています。詳しくはこちらに書きました。
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと変更されてた - メモ的な思考的な

なお、本文はSelenium2Libraryのままにしてあります。

2017/9/2追記ここまで

 
目次

 

環境

 

Seleniumで開くChromeDriverに拡張を入れる 

packedとunpacked、どちらの方式の拡張を入れるか

Seleniumで使うChromeDriverに拡張を入れる方法は、ChromeDriverの公式サイトにありました。
Chrome Extensions - ChromeDriver - WebDriver for Chrome

方法としては、

の2つがあるようです。

紹介されているコードはJavaっぽいため、Pythonのコードを探したところ、前者については以下などがありました。
google chrome - Running Selenium WebDriver using Python with extensions (.crx files) - Stack Overflow

一方、後者のDesiredCapabilitiesオブジェクトの setCapability() メソッドを使う方法を調べてみましたが、それらしい事例はなく、Pythonライブラリのソースコードにもそれらしいメソッドは見当たりませんでした。
selenium/desired_capabilities.py at master · SeleniumHQ/selenium

そこで今回は、前者の crxファイルを使う方法で実装することにしました。

 

packedな拡張を作成する方法

Chrome拡張がどこにインストールされるかを調べたところ、 ~/Library/Application Support/Google/Chrome のようでした。
User Data Directory

また、Chrome拡張のID(ディレクトリ名)は、以下を参考にして確認しました。
Google Chrome のアプリや拡張機能、テーマが保存されている場所 - Qiita

そのディレクトリを見たところ、unpackedな状態だったため、何らかの方法で crxファイルを取得する必要がありました。

 
調べてみたところ、以下のページにpackedな拡張の作り方が記載されていました。
CRX Package Format - Google Chrome

ただ、pemファイルを用意するなどの手間がかかりそうなため、もっと容易な方法を探してみたところ、Chrome拡張のページからcrxファイルを取得するChrome拡張 Get CRX がありました。
Get CRX - Chrome ウェブストア

試しに Referer Control のページで使ってみたところ、crxファイルが取得できました。

今回はそのファイルを、テストを実行するディレクトリ直下に referer_control.crx として保存します。

 

Robot Frameworkで実装する

HeadlessなChromeだとChrome拡張ページが開けないため、今回は普通のChromeを使って実装します。

${options} =  evaluate  sys.modules['selenium.webdriver'].ChromeOptions()  sys

# HeadlessだとChrome拡張ページが開けない
# call method  ${options}  add_argument  --headless

# ${OUTPUT DIR}は、テストを実行するディレクトリ
call method  ${options}  add_extension  ${OUTPUT DIR}/referer_control.crx
create webdriver  Chrome  chrome_options=${options}

 

Referer Controlでリファラを書き換える

上記のままではChrome拡張を入れるだけで何も設定がされないため、Referer Controlの設定を行います。

 

Referer Controlの設定方法を調べる

以下が参考になりました。
Chromeでリファラ偽装する方法(リファラコントロールの使い方)

 

Robot Frameworkで実装する

上記の通り、Referer Controlの設定はブラウザから行うため、Robot Frameworkで実装できそうでした。

Robot Frameworkのlocatorに何を使うか悩みましたが、Chrome拡張のHTMLはそれほど変わらないだろうと考えました。

そこで、Chrome Developer Toolを使ってXPathを取得し、それをlocatorとして使いました。*1

あとは、Robot Frameworkで実装します。

今回は http://localhost:8084/referer/target というURLの場合のみ、リファラhttps://www.google.co.jp へと書き換えるようにしました。

# 「Referer Control」拡張の設定ページを開く
go to  chrome-extension://hnkcfpcejkafcihlgbojoidoihckciin/chrome/content/options.html

wait until page contains element  xpath=//*[@id="settingsTable"]/tbody/tr/td[2]/input
click element  xpath=//*[@id="settingsTable"]/tbody/tr/td[2]/input

# site filterに入力する
# 今回は、「http://localhost:8084/referer/target」というサイトであればリファラを書き換える
input text     xpath=//*[@id="settingsTable"]/tbody/tr/td[2]/input  http://localhost:8084/referer/target

# Customボタンを押す
click element  xpath=//*[@id="settingsTable"]/tbody/tr/td[8]

# Custom refererを入力する(Googleから来たことにする)
# 1秒待たないとうまくいかない
sleep  1s
input text  xpath=//*[@id="settingsTable"]/tbody/tr[5]/td[2]/table/tbody/tr[1]/td[1]/input  https://www.google.co.jp

# 適当なところをクリックして保存
click element  xpath=//*[@id="settingsTable"]/tbody/tr/td[2]/input

 

Bottleアプリの実装と動作確認

Bottleアプリの実装

上記のRobot Frameworkのテストコードが正しく動作するかを確認するため、Bottleアプリを作成して動作を確認します。

今回は、単純にリクエストヘッダの Referer を表示するだけにします。

target.py

@get('/referer/target')
@get('/referer/exclude')
def get_referer_target():
    referer = request.get_header('Referer')
    return f'<p id="referer">Referer: {referer}</p>'

 

Robot Frameworkで動作確認するコードを実装

先ほどのChrome拡張の設定に続いて以下のコードを書きます。

# リファラが書き換わるページへアクセス
go to  http://localhost:8084/referer/target
${target} =  get text  id=referer

# リファラが書き換わらないページへアクセス
go to  http://localhost:8084/referer/exclude
${exclude} =  get text  id=referer

# 検証してブラウザを閉じる
log to console  ${EMPTY}
log to console  ${target}
log to console  ${exclude}
should not be equal  ${target}  ${exclude}
close browser

 

動作確認

テストがパスしました。Chrome拡張の設定がうまくいき、リファラが書き換わっているようです。

$ robot selenium_modify_referer_test.robot 
===================================
Selenium Modify Referer Test                   
===================================
Chrome拡張でリファラを書き換えるテスト                                
Referer: https://www.google.co.jp
Referer: None
Chrome拡張でリファラを書き換えるテスト | PASS |
...

 

その他参考

Firefoxを使う場合

Firefoxを使う場合の例が以下にありました。
webdriver - setting request headers in selenium - Stack Overflow

 

Chromeコマンドライン・オプション

以下にまとまっていました。
List of Chromium Command Line Switches « Peter Beverloo

 

OpenSSLでのpemファイル作成

もしpemファイルを作る場合は、以下を参考にします。
ssl - How to create a self-signed certificate with openssl? - Stack Overflow

 

ソースコード

GitHubに上げました。 selenium2_library_sample/tests/selenium_modify_referer_test.robot ファイルが今回のテストファイルです。
thinkAmi-sandbox/RobotFramework-sample: Robot Framewrok samples

*1:該当Element上で右クリック、Copy > Copy XPathXpathを取得できます

「Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版」をBoto3とAnsibleで写経してみた

社内ではAWSが普通に使われているため、常々基礎からきちんと学びたいと考えていました。

そんな中、書籍「Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版」の社内勉強会が開催されることになりました。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

 
これはちょうどよい機会だと思い、書籍をひと通り読んでみました。

書籍では、オンプレでやってたことをAWSで実現するにはどうすればよいかなど、AWSのインフラまわりの基礎的なところが書かれており、とてもためになりました。

ただ、手動でAWS環境を構築したため、このまま何もしないと忘れそうでした。

何か良い方法がないかを探したところ、AWS SDK for Python(Boto3)がありました。これを使ってPythonスクリプトとして残しておけば忘れないだろうと考えました。

 
なお、Boto3ではなくAnsibleでも構築できそうでしたが、今回はBoto3の使い方に慣れようと思い、

として、写経してみることにしました。

 
目次

 

環境

現在では、Boto3・AnsibleともにPython3で動くようです。

 

IAMユーザーの作成と設定

書籍では、AWSアカウントについては特に触れられていませんでした。

そこで、今年の3/11に開かれたJAWS DAYS 2017のセッション「不安で夜眠れないAWSアカウント管理者に送る処方箋という名のハンズオン」に従い、IAMユーザーを作成・使用することにしました。

 
主な設定内容は

としました。

 

Boto3の準備

今回はawscliを使わず、Boto3だけで環境を準備します。

READMEのQuick Startに従って、環境ファイルを用意します。
boto/boto3: AWS SDK for Python

~/.aws/credentials

こちらに aws_access_key_idaws_secret_access_keyを設定します。
【鍵管理】~/.aws/credentials を唯一のAPIキー管理場所とすべし【大指針】 | Developers.IO

今回は試しにデフォルト以外のprofile(my-profile)を使うことにしてみました。
boto3 で デフォルトprofile以外を使う - Qiita

[my-profile]
aws_access_key_id = YOUR_KEY
aws_secret_access_key = YOUR_SECRET

 

~/.aws/config

こちらにはデフォルトリージョンを設定します。

READMEにはus-east-1と記載されています。

東京リージョンの場合は何を指定すればよいかを探したところ、 ap-northeast-1 を使うのが良さそうでした。
AWS のリージョンとエンドポイント - アマゾン ウェブ サービス

[default]
region=ap-northeast-1

ただ、自分の書き方が悪いせいか、上記のように書いてもデフォルトリージョンとして設定されなかったため、Boto3を使うところでリージョンを指定しています。

 
あとは、

# ~/.aws/credentialsのmy-profileにあるキーを使う
session = boto3.Session(profile_name='my-profile')

# Clientを使う場合
client = session.client('ec2', region_name='ap-northeast-1')

# Resourceを使う場合
resource = session.resource('ec2', region_name='ap-northeast-1')

のようにして、アクセスキーなどを持ったclientとresourceを取得し、それを使って操作します。

なお、clientやresourceの第一引数にはAWS サービスの名前空間を使えば良さそうでした。
AWS サービスの名前空間 | Amazon リソースネーム (ARN) と AWS サービスの名前空間 - アマゾン ウェブ サービス

今回使用するAmazon EC2Amazon VPC名前空間は、ともに ec2 のようでした。

 

Ansibleまわり

途中でEC2の設定をするため、Ansibleまわりの準備もしておきます。
ansible.cnfでssh_configを設定する | Developers.IO

 

ssh_config

HostNameのxxx.xxx.xxx.xxxは、EC2を立てた時にパブリックIPアドレスへと差し替えます。

Host webserver
  User ec2-user
  HostName xxx.xxx.xxx.xxx
  # IdentityFileはカレントディレクトリに置いておけば良い
  IdentityFile syakyo_aws_network_server2.pem
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null

 

ansible.cnf

Fオプションのファイル(ssh_config)は、プルパスでなくても問題ありませんでした。

[ssh_connection]
ssh_args = -F ssh_config

 

Inventoryファイル(hosts)

ここに記述するホスト名は、ssh_configのHostの値と一致させます。今回はwebserverとなります。

[web]
webserver

 
あとは、ターミナルから

$ ansible-playbook -i hosts ch4_apache.yml

のようにして、EC2インスタンスに対して実行します。

 

写経

今回写経したコードを載せておきます。

なお、だいたいのものはClientとResourceServiceのどちらでも操作できました。

ただ、戻り値が

  • Clientは、dict
  • ResourceServiceは、各オブジェクト

だったので、ResourceServiceの方が扱いやすいのかなと感じました。

 

Chapter2
VPCの作成

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_vpc

response = ec2_client.create_vpc(
    CidrBlock='192.168.0.0/16',
    AmazonProvidedIpv6CidrBlock=False,
)
vpc_id = response['Vpc']['VpcId']

 

VPCに名前をつける

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Vpc.create_tags

vpc = ec2_resource.Vpc(vpc_id)
tag = vpc.create_tags(
    Tags=[
        {
            'Key': 'Name',
            'Value': 'VPC領域2'
        },
    ]
)

 

VPCの一覧を確認

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_vpcs

response = ec2_client.describe_vpcs(
    Filters=[
        {
            'Name': 'tag:Name',
            'Values': [
                'VPC領域',
            ]
        }
    ]
)

 

アベイラビリティゾーンの確認

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_availability_zones

response = ec2_client.describe_availability_zones(
    Filters=[{
        'Name': 'state',
        'Values': ['available'],
    }]
)

 

VPCにサブネットを作成

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Vpc.create_subnet

vpc = ec2_resource.Vpc(vpc_id)
response = vpc.create_subnet(
    AvailabilityZone=availability_zone,
    CidrBlock=cidr_block,
)

 

サブネットに名前をつける

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Subnet.create_tags

tag = ec2_subnet.create_tags(
    Tags=[{
        'Key': 'Name',
        'Value': subnet_name,
    }]
)

 

インターネットゲートウェイを作成

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_internet_gateway

response = ec2_client.create_internet_gateway()

 

インターネットゲートウェイに名前をつける

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.InternetGateway.create_tags

internet_gateway = ec2_resource.InternetGateway(internet_gateway_id)
tags = internet_gateway.create_tags(
    Tags=[{
        'Key': 'Name',
        'Value': 'インターネットゲートウェイ2',
    }]
)

 

インターネットゲートウェイVPCに紐づける

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.InternetGateway.attach_to_vpc

internet_gateway = ec2_resource.InternetGateway(internet_gateway_id)
response = internet_gateway.attach_to_vpc(
    VpcId=vpc_id,
)

 

ルートテーブルを作成する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_route_table

response = ec2_client.create_route_table(VpcId=vpc_id)

 

ルートテーブルに名前をつける
route_table = ec2_resource.RouteTable(route_table_id)
tag = route_table.create_tags(
    Tags=[{
        'Key': 'Name',
        'Value': 'パブリックルートテーブル2',
    }]
)

 

ルートテーブルをサブネットに割り当てる

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.RouteTable.associate_with_subnet

route_table = ec2_resource.RouteTable(route_table_id)
route_table_association = route_table.associate_with_subnet(SubnetId=subnet_id)

 

デフォルトゲートウェイをインターネットゲートウェイに割り当てる

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.RouteTable.create_route

route_table = ec2_resource.RouteTable(route_table_id)
route = route_table.create_route(
    DestinationCidrBlock='0.0.0.0/0',
    GatewayId=internet_gateway_id,
)

 

ルートテーブルを確認する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_route_tables

response = ec2_client.describe_route_tables()

 

Chapter3
キーペアを作成してローカルに保存する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_key_pair

# キーペアを作成していない場合はキーペアを作成する
if not os.path.exists(KEY_PAIR_FILE):
    response = ec2_client.create_key_pair(KeyName=KEY_PAIR_NAME)
    print(inspect.getframeinfo(inspect.currentframe())[2], response['KeyName'])
    with open(KEY_PAIR_FILE, mode='w') as f:
        f.write(response['KeyMaterial'])

 

ローカルに保存したキーのパーミッションを変更する
# modeは8進数表記がわかりやすい:Python3からはprefixが`0o`となった
os.chmod(KEY_PAIR_FILE, mode=0o400)

 

セキュリティグループを作成する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_security_group

response = ec2_client.create_security_group(
    Description=name,
    GroupName=name,
    VpcId=vpc_id,
)

 

セキュリティグループでSSHのポートを開ける
security_group = ec2_resource.SecurityGroup(security_group_id)
response = security_group.authorize_ingress(
    CidrIp='0.0.0.0/0',
    IpProtocol='tcp',
    # ポートは22だけ許可したいので、From/Toともに22のみとする
    FromPort=22,
    ToPort=22,
)

 

EC2インスタンスを立てる
response = ec2_resource.create_instances(
    ImageId=IMAGE_ID,
    # 無料枠はt2.micro
    InstanceType='t2.micro',
    # 事前に作ったキー名を指定
    KeyName=key_pair_name,
    # インスタンス数は、最大・最小とも1にする
    MaxCount=1,
    MinCount=1,
    # モニタリングはデフォルト = Cloud Watchは使わないはず
    # Monitoring={'Enabled': False},
    # サブネットにavailability zone が結びついてるので、明示的なセットはいらないかも
    # Placement={'AvailabilityZone': availability_zone},
    # セキュリティグループIDやサブネットIDはNetworkInterfacesでセット(詳細は以下)
    # SecurityGroupIds=[security_group_id],
    # SubnetId=subnet_id,
    NetworkInterfaces=[{
        # 自動割り当てパブリックIP
        'AssociatePublicIpAddress': is_associate_public_ip,
        # デバイスインタフェースは1つだけなので、最初のものを使う
        'DeviceIndex': 0,
        # セキュリティグループIDは、NetworkInterfacesの方で割り当てる
        # インスタンスの方で割り当てると以下のエラー:
        # Network interfaces and an instance-level security groups may not be specified on the same request
        'Groups': [security_group_id],
        # プライベートIPアドレス
        'PrivateIpAddress': private_ip,
        # サブネットIDも、NetworkInterfacesの方で割り当てる
        # インスタンスの方で割り当てると以下のエラー:
        # Network interfaces and an instance-level subnet ID may not be specified on the same request
        'SubnetId': subnet_id,
    }],
    TagSpecifications=[{
        'ResourceType': 'instance',
        'Tags': [{
            'Key': 'Name',
            'Value': instance_name,
        }]
    }],
)

 

EC2インスタンスがrunningになるまで待つ

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Instance.wait_until_running

ec2_instance.wait_until_running()

 

Chapter4
GUIの「DNSホスト名の編集」を実行する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.modify_vpc_attribute

response = ec2_client.modify_vpc_attribute(
    EnableDnsHostnames={'Value': True},
    VpcId=vpc_id,
)

 

AnsibleでApacheをインストールし、起動設定にする
# hostsにはhostsファイルの[web]かホスト名(webserver)を指定する
- hosts: webserver
  become: yes
  tasks:
    - name: install Apache
      yum: name=httpd
    - name: Apache running and enabled
      service: name=httpd state=started enabled=yes

 

Chapter6

Chapter6ではプライベートサブネットにEC2インスタンスを立てます。

その方法はパブリックサブネットと同様なため、ここでは差分のみ記載します。

 

パブリックサブネットのAvailability Zoneを取得する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#subnet

ec2_subnet = ec2_resource.Subnet(subnet_id)
return ec2_subnet.availability_zone

 

セキュリティグループでICMPのポートを開ける

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.SecurityGroup.authorize_ingress

security_group = ec2_resource.SecurityGroup(security_group_id)
response = security_group.authorize_ingress(
    CidrIp='0.0.0.0/0',
    IpProtocol='icmp',
    # ICMPの場合、From/Toともに -1 を設定
    FromPort=-1,
    ToPort=-1,
)

 

Ansibleで、ローカルのSSH鍵をWebサーバへSCPを使って転送する
# hostsにはhostsファイルの[web]かホスト名(webserver)を指定する
- hosts: webserver
  tasks:
    - name: copy private-key to webserver
      copy: src=./syakyo_aws_network_server2.pem dest=~/ owner=ec2-user group=ec2-user mode=0400

 

Chapter7
Elastic IPを取得する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.allocate_address

# DomainのvpcはVPC、standardはEC2-Classic向け
response = ec2_client.allocate_address(Domain='vpc')

 

NATゲートウェイを作成し、Elastic IPを割り当て、パブリックサブネットに置く

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_nat_gateway

response = client.create_nat_gateway(
    AllocationId=allocation_id,
    SubnetId=subnet_id,
)

 

NATゲートウェイがavailableになるまで待つ

NATゲートウェイを作成した直後はまだavailableになっていないため、availableになるまで待ちます。

手元では約2分ほどかかりました。

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Waiter.NatGatewayAvailable

waiter = ec2_client.get_waiter('nat_gateway_available')
response = waiter.wait(
    Filters=[{
        'Name': 'state',
        'Values': ['available']
    }],
    NatGatewayIds=[nat_gateway_id]
)

 

メインのルートテーブルのIDを取得する

NATゲートウェイのエントリを追加するため、メインのルートテーブルのIDを取得します。

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_route_tables

response = ec2_client.describe_route_tables(
    Filters=[
        {
            'Name': 'association.main',
            'Values': ['true'],
        },
        {
            'Name': 'vpc-id',
            'Values': [vpc_id],
        }
    ]
)
main_route_table_id = response['RouteTables'][0]['RouteTableId']

 

メインのルートテーブルにNATゲートウェイのエントリを追加する

https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.RouteTable.create_route

route_table = ec2_resource.RouteTable(route_table_id)
route = route_table.create_route(
    DestinationCidrBlock='0.0.0.0/0',
    NatGatewayId=nat_gateway_id,
)

 

写経時に作成したものを削除する

ここまででGUIで行った内容をBoto3 & Ansibleで実装しました。

せっかくなので、作成したものをBoto3で削除するPythonスクリプトも作成してみました (boto3_ansible/clear_all.py)。

流れとしては、作成したのとは逆順で削除していく形となります。

 

その他参考

 

ソースコード

GitHubに上げました。
thinkAmi-sandbox/syakyo-aws-network-server-revised-edition-book

Robot Frameworkにて使える時間のフォーマットについて調べてみた

Robot Frameworkでは、 sleep 1 seconds のように、数値 + 単位で時間を表現できます。

ただ、サイトによって secondssが使われるなど、単位のフォーマットが異なっていました。

そこで今回、どういうフォーマットが使えるのか調べてみました。

 

環境

 

調査結果

公式サイトに記載がありました。

・days, day, d

・hours, hour, h

・minutes, minute, mins, min, m

・seconds, second, secs, sec, s

・milliseconds, millisecond, millis, ms

6.5 Time format | Robot Framework User Guide

また、 4 s4s のように、数字と単位は、離す・くっつけるのどちらでも良いようです。

 
試してみます。

なお、

とします。

*** Settings ***

Library  DateTime


*** Keywords ***
現在時刻を出力する
    [Arguments]  ${label}=start
    ${now} =  get current date
    log to console  ${now}${SPACE * 3}[${label}]


*** TestCases ***
引数の秒数のフォーマットを確認する
    log to console  ${SPACE}

    現在時刻を出力する

    sleep  1 seconds
    現在時刻を出力する  label=seconds

    sleep  1 second
    現在時刻を出力する  label=second

    sleep  1 secs
    現在時刻を出力する  label=secs

    sleep  1 sec
    現在時刻を出力する  label=sec

    sleep  1 s
    現在時刻を出力する  label=s

    sleep  1sec
    現在時刻を出力する  label=sec_without_space

    sleep  1s
    現在時刻を出力する  label=s_without_space

    sleep  1.5s
    現在時刻を出力する  label=s_with_dot

 
結果です。いずれのフォーマットでも正しく動作しているようです。

$ robot datetime_library_sample/time_format_test.robot 
...
==============================================================================
Time Format Test                                                              
==============================================================================
引数の秒数のフォーマットを確認する                                     
.2017-08-11 07:38:36.420   [start]
..2017-08-11 07:38:37.424   [seconds]
..2017-08-11 07:38:38.427   [second]
..2017-08-11 07:38:39.431   [secs]
引数の秒数のフォーマットを確認する                                    .2017-08-11 07:38:40.435   [sec]
..2017-08-11 07:38:41.438   [s]
..2017-08-11 07:38:42.441   [sec_without_space]
..2017-08-11 07:38:43.444   [s_without_space]
引数の秒数のフォーマットを確認する                                    .2017-08-11 07:38:44.947   [s_with_dot]
引数の秒数のフォーマットを確認する                                    | PASS |
...

 

ソースコード

GitHubに上げました。datetime_library_sampleの中にあるtime_format_test.robotファイルが今回のテストファイルです。
thinkAmi-sandbox/RobotFramework-sample

Robot Framework + Selenium2Libraryで、「Go To」を使う時はURIスキームを付ける

Selenium2Libraryでは、 Go To キーワードを使って引数のURLに移動します。
Go To | Selenium2Library

 
今までのサンプルでは、localhostへの移動はURIスキームを省略した localhost:8084 みたいな値を引数として渡していました。

 
しかし、通常のChromeとHeadlessなChromeでは挙動が異なったため、Go Toを使う時はURIスキームを付けておくべきと感じました。

その例をメモしておきます。

 
2017/9/2追記

Selenium2Libraryですが、バージョン3からは SeleniumLibrary へと名称が変更されています。詳しくはこちらに書きました。
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと変更されてた - メモ的な思考的な

なお、本文はSelenium2Libraryのままにしてあります。

2017/9/2追記ここまで

 
目次

 

環境

  • Mac OS X 10.11.6
  • Python 3.6.2
  • Google Chrome 60.0.3112.78 (stable)
  • ChromeDriver 2.31 (stable)
  • RobotFramework 3.0.2
  • Selenium2Library 1.8.1dev1
    • GitHubから現在の最新版をインストール
    • PyPIにある1.8.0では、Python3系だとエラーが出てインストールできない
  • Selenium 3.4.3
  • Bottle 0.12.13
    • Webアプリ用

 

キーワードの用意

今回、通常のChromeとHeadlessなChromeでの挙動を比較するため、以下のキーワードを用意します。

*** Keywords ***
URIスキーム無しでlocalhostへアクセスする
    go to  localhost:8084/
    location should be  http://localhost:8084/

URIスキームありでlocalhostへアクセスする
    go to  http://localhost:8084/
    location should be  http://localhost:8084/

URIスキーム無しでGoogleへアクセスする
    go to  www.google.co.jp/
    location should be  https://wwww.google.co.jp/

URIスキームありでGoogleへアクセスする
    go to  https://www.google.co.jp/
    location should be  https://www.google.co.jp/

キーワード「${キーワード}」が失敗することを確認する
    ${エラー有無}  ${エラーメッセージ} =  run keyword and ignore error  ${キーワード}
    should be equal  ${エラー有無}  FAIL
    log to console  エラーメッセージ:\n${エラーメッセージ}\n_______________

 

通常のChromeの場合

通常のChromeの場合、localhostへはURIスキームの有無に関係なくアクセスできます。一方、Googleへはアクセスできません。

ノーマルなChromeでURIスキームを扱う
    create webdriver  Chrome
    log to console  ${SPACE}

    URIスキーム無しでlocalhostへアクセスする
    URIスキームありでlocalhostへアクセスする
    キーワード「URIスキーム無しでGoogleへアクセスする」が失敗することを確認する
    URIスキームありでGoogleへアクセスする

実行結果です。

ノーマルなChromeでURIスキームを扱う                                   . 
...エラーメッセージ:
WebDriverException: Message: unknown error: unhandled inspector error: {"code":-32000,"message":"Cannot navigate to invalid URL"}
  (Session info: chrome=60.0.3112.90)
  (Driver info: chromedriver=2.31.488774 (7e15618d1bf16df8bf0ecf2914ed1964a387ba0b),platform=Mac OS X 10.11.6 x86_64)
_______________

 

HeadlessなChrome

通常のChromeと異なり、localhostの場合にもURIスキームが必要になります。

HeadlessなChromeでURIスキームを扱う
    ${options} =  evaluate  sys.modules['selenium.webdriver'].ChromeOptions()  sys
    call method  ${options}  add_argument  --headless
    create webdriver  Chrome  chrome_options=${options}
    log to console  ${SPACE}

    キーワード「URIスキーム無しでlocalhostへアクセスする」が失敗することを確認する
    URIスキームありでlocalhostへアクセスする
    キーワード「URIスキーム無しでGoogleへアクセスする」が失敗することを確認する
    URIスキームありでGoogleへアクセスする

実行結果です。

HeadlessなChromeでURIスキームを扱う                                   ... 
.エラーメッセージ:
Location should have been 'http://localhost:8084/' but was 'data:,'
_______________
..エラーメッセージ:
WebDriverException: Message: unknown error: unhandled inspector error: {"code":-32000,"message":"Cannot navigate to invalid URL"}
  (Session info: headless chrome=60.0.3112.90)
  (Driver info: chromedriver=2.31.488774 (7e15618d1bf16df8bf0ecf2914ed1964a387ba0b),platform=Mac OS X 10.11.6 x86_64)
_______________

 
これらより、キーワード Go To を使う場合はURIスキームを付けるほうが、エラーが出ることなく移動できることが分かりました。

 

ソースコード

GitHubに上げました。selenium2_library_sample/tests/の中にあるselenium_uri_scheme_test.robotファイルが今回のテストファイルです。
thinkAmi-sandbox/RobotFramework-sample

Robot Framework + Selenium2Library + robotframework-requestsで、静的ファイルが本当にあるのか確認する

以前、Robot Framework + Selenium2Libraryで、静的HTMLをテストしてみました。
Robot Framework + Selenium2Libraryで、静的HTMLをテストする - メモ的な思考的な

 
その時、

<img id="img_200" src="/static/image/a.png" alt="あの字" title="画像タイトル">

というHTMLに対し、

page should contain image  jquery=img[alt='あの字']

というテストコードを書きました。

 
しかし、実際にはそのテストコードは静的ファイルが存在するかまではチェックしていないため、

<!--存在する画像ファイル-->
<img id="img_200" src="/static/image/200.png" alt="存在します" title="画像タイトル">

<!--存在しない画像ファイル-->
<img id="img_404" src="/static/image/404.png" alt="存在しません" title="画像タイトル2">

というHTMLがあり、実際には

f:id:thinkAmi:20170809202012p:plain:w200

となっていた場合でも、

page should contain image  id=img_404
element should be visible  id=img_404

というテストがパスしてしまいます。

 
静的ファイルが本当にあるのかを確認する方法を探したところ、stackoverflowに robotframework-requests を使う方法が書かれていました。内部で requests を使っているようです。

そのため、今回 robotframework-requests を試してみます。

 
2017/9/2追記

Selenium2Libraryですが、バージョン3からは SeleniumLibrary へと名称が変更されています。詳しくはこちらに書きました。
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと変更されてた - メモ的な思考的な

なお、本文はSelenium2Libraryのままにしてあります。

2017/9/2追記ここまで

 
目次

 

環境

  • Mac OS X 10.11.6
  • Python 3.6.2
  • Google Chrome 60.0.3112.78 (stable)
  • ChromeDriver 2.31 (stable)
  • RobotFramework 3.0.2
  • robotframework-requests 0.4.7
  • Selenium2Library 1.8.1dev1
    • GitHubから現在の最新版をインストール
    • PyPIにある1.8.0では、Python3系だとエラーが出てインストールできない
  • Selenium 3.4.3
  • Bottle 0.12.13
    • Webアプリ用

 

実装

GitHubにあるREADMEのUsageに使い方があるため、それに従って実装してみます。

SettingsにLibraryを追加

今回、Selenium2Libraryも使うため、2つをSettingsに追加します。

*** Settings ***
Library  Selenium2Library
Library  RequestsLibrary

 

imgタグのsrc属性から、URLを取得

Get Element Attributeキーワードを使って、imgタグのsrc属性から対象の静的ファイルのURLを取得します。
Get Element Attribute | Selenium2Library

${src404} =  get element attribute  id=img_404@src
log to console  ${src404}
# => http://localhost:8084/static/image/404.png

 

セッションオブジェクトを作成

requestsと同様、 requests.Session オブジェクトを作成します。
Create Session | RequestsKeywords

今回は任意のセッション名とURLのみ指定していますが、他にもCookieなどいろいろと指定できますので、詳しくはドキュメントを見てください。

# 第一引数は任意のセッション名、第二引数はURL
create session  not-found-session  ${src404}

 

画像のURLをGET

stackoverflowにはGetキーワードが書かれています。

しかし、

  • ドキュメントでは Deprecated
  • 実行すると、 Deprecation Warning: Use Get Request in the future ワーニング

ですので、キーワード Get Request を代わりに使います。
Get Request | RequestsKeywords

なお、上記のstackoverflowによると、URLは URL is relative to the session URL とのことなので、今回も同じように / を指定します。

${response404} =  get request  not-found-session  /
log to console  ${response404}
#=> <Response [404]>

 

ステータスコードの検証

上記の通り、Get Requestキーワードの戻り値はResponseオブジェクトになります。

そのため、requestsのResponseオブジェクトの属性 status_code を使って、ステータスコードを検証します。
status_code - class requests.Response | Developer Interface — Requests 2.18.3 documentation

 
なお、検証で使うキーワードは Should Be Equal As IntegersShould Be Equal As Strings 、どちらでも同じ結果になります。

should be equal as integers  ${response404.status_code}  404
should be equal as strings  ${response404.status_code}  404

 
以上で、ファイルが存在しないことを確認できました。

 
なお、静的ファイルが存在することを確認するには、

${src200} =  get element attribute  id=img_200@src
create session  found-session  ${src200}
${response200} =  get request  found-session  /
should be equal as strings  ${response200.status_code}  200

となります。

 

ソースコード

GitHubに上げました。selenium2_library_sample/tests/の中にあるselenium_requests_test.robotファイルが今回のテストファイルです。
thinkAmi-sandbox/RobotFramework-sample

Robot Framework + Selenium2Libraryで、HTML + JavaScriptまわりをテストする

前回、Robot Framework + Selenium2Libraryで、HTMLのフォームまわりをテストしてみました。
Robot Framework + Selenium2Libraryで、HTMLフォームまわりをテストする - メモ的な思考的な

 
今回は、Robot Framework + Selenium2Libraryで、HTML + JavaScriptまわりをテストしてみます。

 
2017/9/2追記

Selenium2Libraryですが、バージョン3からは SeleniumLibrary へと名称が変更されています。詳しくはこちらに書きました。
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと変更されてた - メモ的な思考的な

なお、本文はSelenium2Libraryのままにしてあります。

2017/9/2追記ここまで

 
目次

 

環境

  • Mac OS X 10.11.6
  • Python 3.6.2
  • Google Chrome 60.0.3112.78 (stable)
  • ChromeDriver 2.31 (stable)
  • RobotFramework 3.0.2
  • Selenium2Library 1.8.1dev1
    • GitHubから現在の最新版をインストール
    • PyPIにある1.8.0では、Python3系だとエラーが出てインストールできない
  • Selenium 3.4.3
  • Bottle 0.12.13
    • Webアプリ用

 

Elementが出てくるまで待つ

<div id="show_delay"></div>

というHTMLに対し、

window.addEventListener("load", function () {
    setTimeout(function(){
        document.getElementById('show_delay').innerHTML = '<p id="hello">Hello, world!</p>'
    }, 3000);
});

と、3秒後にp要素を追加するJavaScriptがあるとします。

 
この場合、Wait Until Page Contains Elementを使い、p要素が出てくるまで待機します。
Wait Until Page Contains Element | Selenium2Library

# p要素が出てくるまで待つ
wait until page contains element  id=hello

# 出てきたら、p要素のテキストが正しいかを確認
element text should be  id=hello  Hello, world!

 

マウスオーバーすると出てくるElementをクリックする

<div id="on_mouse_area">このあたりをマウスオーバーするとボタンが見える
    <button id="mouse_target" style="display: none;">
        mouseoverのみ見える
    </button>
</div>

というdisplay: noneなHTMLがあり、

var on_mouse_area = document.getElementById('on_mouse_area');
on_mouse_area.onmouseover = function () {
    document.getElementById('mouse_target').style.display = 'block';
};
on_mouse_area.onmouseout = function () {
    document.getElementById('mouse_target').style.display = 'none';
};
var mouse_target = document.getElementById('mouse_target');
mouse_target.onclick = function () {
    window.location.href = '/'
};

と、マウスオーバー時に表示するJavaScriptがあるとします。

 
この場合、Mouse Overを使いマウスオーバーした後、Click Elementします。
Mouse Over | Selenium2Library

# マウスオーバーする
mouse over  id=on_mouse_area

# クリックする
click element  id=mouse_target

 

スクロールしてクリックする

JavaScriptでスクロールする
<div class="bottom" style="padding-top: 1200px">
    <button id="bottom_button">下にあるボタン</button>
</div>

と、縦長のページの一番下にボタンがあるとします。

 
この場合、JavaScriptを実行するExecute Javascriptを使い、JavaScriptwindow.scrollTo()などを使います。

# スクロールする
Execute JavaScript  window.scrollTo(0, 1200)

# クリックする
click element  id=bottom_button

 

画面サイズを変更する

スクロールするとは関係ないですが、画面サイズについて、

などがあります。

# 現在の画面サイズを取得する
${幅}  ${高さ} =  get window size

# 画面サイズを変更する
set window size  10  10

# 画面サイズを元に戻す
set window size  ${幅}  ${高さ}

 

display:noneなボタンをクリックする

<button id="display_none" style="display: none;">見えない</button>

という、display:noneなStyleのボタンがあるとします。

この場合、JavaScriptを使うことで、強引にクリックすることができます。
Example to click on element which is hidden using Execute Javascript? - Google グループ

execute javascript  document.getElementById('display_none').click();

 

JavaScriptによるリロードがあったことを確認する

Assign Id To Elementを使う
<button id="run_reload" class="reload_element">リロード</button>

というHTMLがあり、このボタンを押すと

var reload = document.getElementById('run_reload');
reload.onclick = function () {
    location.reload();
};

と、リロードするJavaScriptがあるとします。

 
この場合、Assign Id To Elementを使って

  • リロード前にタグを設定
  • リロード後にタグが存在しないことを確認

を行います。

Assign Id To Elementで設定したidはリロードすると消えてしまう特性を利用します。
Assign Id To Element | Selenium2Library

# id="run_reload"をid="not_reload"に付け替える
assign id to element  run_reload  not_reload

 

Assign Id To Elementの注意点
assign id to element  id=reload_dummy  not_reload

と、locatorにidを指定すると、

Keyword 'Selenium2Library.Assign Id To Element' got positional argument after named arguments.

というエラーになります。

一方、locatorがclassやxpathであればエラーは出ません。

assign id to element  class=reload_element  not_reload

どうしてもlocatorにidを指定したい場合は、id=を省略すると、デフォルトの挙動であるidnameで検索してくれます。

assign id to element  run_reload  not_reload

 
また、Assign Id To Elementはidを付け替えるため、

<button id="run_reload" class="reload_element">リロード</button>

というHTMLの場合、

# idの割り当て
assign id to element  run_reload  not_reload

# not_reload なidは存在する
page should contain element  id=not_reload

# run_reload なidが存在しない
page should not contain element  id=run_reload

という挙動となります。

idの付け替えが困る場合には、idがないElementなど、他の適当なElementを指定します。

 

JavaScriptのalertまわり

alertのOKボタンを押す
<button id="show_alert">警告</button>

というHTMLに対し、

var show_alert = document.getElementById('show_alert');
show_alert.onclick = function () {
    alert('OKのみです');
};

とOKのみのalertを表示するJavaScriptがあるとします。

 
この場合、Dismiss Alertを使い、alertのOKボタンを押します。
Dismiss Alert | Selenium2Library

# alertを表示する
click element  id=show_alert

# alertでOKを押す
dismiss alert

 

alertのメッセージを取得してOKを押す

同じく、

<button id="show_alert">警告</button>

というHTMLに対し、

var show_alert = document.getElementById('show_alert');
show_alert.onclick = function () {
    alert('OKのみです');
};

というJavaScriptがあった場合、メッセージの内容を取得してからOKを押したいとします。

 
この場合、Get Alert Messageを使い、alertのメッセージを取得します。
Get Alert Message | Selenium2Library

# alertを表示する
click element  id=show_alert

# alertのメッセージを取得する
${アラートメッセージ} =  get alert message

# alertのメッセージを確認する
should be equal  ${アラートメッセージ}  OKのみです

 

alertが表示されていることを確認してからOKを押す

同じく、

<button id="show_alert">警告</button>

というHTMLに対し、

var show_alert = document.getElementById('show_alert');
show_alert.onclick = function () {
    alert('OKのみです');
};

というJavaScriptがあった場合、alertが表示されていることを確認してからOKを押したいとします。

 
この場合、Alert Should Be Presentを使い、alertが表示されていることを確認してからOKを押します。
Alert Should Be Present | Selenium2Library

# alertを表示する
click element  id=show_alert

# alertが表示されていることを確認してからOKを押す
alert should be present

 

alertの表示とメッセージの一致を確認してからOKを押す

同じく、

<button id="show_alert">警告</button>

というHTMLに対し、

var show_alert = document.getElementById('show_alert');
show_alert.onclick = function () {
    alert('OKのみです');
};

というJavaScriptがあった場合、alertが表示され、メッセージも一致していることを確認してからOKを押したいとします。

 
この場合、Alert Should Be Presentで引数を使い、alert表示とメッセージ一致の確認後にOKを押します。
Alert Should Be Present | Selenium2Library

# alertを表示する
click element  id=show_alert

# alertが表示され、メッセージも一致していることを確認してからOKを押す
alert should be present  OKのみです

 

alertの表示に時間がかかった場合でもOKを押す
<button id="wait_alert">2秒後に警告</button>

というHTMLに対し、

var wait_alert = document.getElementById('wait_alert');
wait_alert.onclick = function () {
    setTimeout(function(){
        alert('2秒後のアラート');
    }, 2000);
};

と、2秒後にalertを表示するJavaScriptがあるとします。

 
この場合、Wait Until Keyword Succeedsとalertを処理するキーワードを併用します。
Wait Until Keyword Succeeds | BuiltIn

 
Wait Until Keyword Succeedsの引数は

  • 第一引数は、リトライ時間
  • 第二引数は、リトライ間隔
  • 第三引数は、実行するキーワード

です。

# 5秒の間、1秒ごとに、「Get Alert Message」キーワードを実行する
${アラートメッセージ} =  wait until keyword succeeds  5 sec  1 sec  get alert message

# alertに「2秒後のアラート」というメッセージがあったことを確認
should be equal  ${アラートメッセージ}  2秒後のアラート

 

JavaScriptのconfirmまわり

confirmのOKを押す
<button id="show_confirm">確認</button>

というHTMLに対し、

var show_confirm = document.getElementById('show_confirm');
show_confirm.onclick = function () {
    var answer = confirm('OKとキャンセルです');
    if (answer){
        alert('OKが押されました');
    }
    else{
        alert('キャンセルされました');
    }
};

と、ボタンを押したらconfirmが表示されるJavaScriptがあるとします。

 
この場合、Confirm Actionを使い、confirmでOKを押します。
Confirm Action | Selenium2Library

ちなみに、Confirm ActionのデフォルトではOKを押します。そのため、デフォルトを変更しない限り、Choose Ok On Next Confirmationは不要です。
Choose Ok On Next Confirmation | Selenium2Library

# OKボタンを押して、confirmのメッセージを取得する
${確認メッセージ} =  confirm action

# confirmのメッセージを確認する
should be equal  ${確認メッセージ}  OKとキャンセルです

# OKを押した時にalertが表示され、「OKが押されました」というメッセージがあるかを確認する
${アラートメッセージ} =  get alert message
should be equal  ${アラートメッセージ}  OKが押されました

 

confirmのキャンセルを押す

同じく、

<button id="show_confirm">確認</button>

というHTMLに対し、

var show_confirm = document.getElementById('show_confirm');
show_confirm.onclick = function () {
    var answer = confirm('OKとキャンセルです');
    if (answer){
        alert('OKが押されました');
    }
    else{
        alert('キャンセルされました');
    }
};

と、ボタンを押したらconfirmが表示されるJavaScriptがあるとします。

 
この場合、Choose Cancel On Next ConfirmationConfirm Actionを使い、confirmでキャンセルを押します。

# 次の確認処理ではキャンセルボタンを押す
choose cancel on next confirmation

# confirmが表示され、キャンセルを押す
${確認メッセージ} =  confirm action
should be equal  ${確認メッセージ}  OKとキャンセルです

# キャンセルを押した時にalertが表示され、「キャンセルされました」というメッセージがあるかを確認する
${アラートメッセージ} =  get alert message
should be equal  ${アラートメッセージ}  キャンセルされました

 

JavaScriptのpromptに入力する

<button id="show_prompt">入力</button>

というHTMLに対し、

var show_prompt = document.getElementById('show_prompt');
show_prompt.onclick = function () {
    var input_message = prompt('値を入力します');
    alert(input_message);
};

と、JavaScriptのpromptが表示されるとします。

 
この場合、Input Text Into Promptを使い、promptに入力します。
Input Text Into Prompt | Selenium2Library

また、Input Text Into Promptはpromptに入力するだけなので、Confirm ActionによりOKボタンを押します。

なお、ここまでの処理でChoose Cancel On Next Confirmationが使われていた場合、Confirm Actionしてもキャンセルボタンが押されてしまうため、promptへの入力が成功しません。

そのため、Choose Cancel On Next Confirmationが使われている可能性がある場合は、明示的にChoose Ok On Next Confirmationを実行した後にConfirm Actionを実行するとよいでしょう。

# promptを表示する
click element  id=show_prompt

# ここまででChoose Cancel On Next Confirmationが使われている可能性があるため、
# 明示的にChoose Ok On Next Confirmationを実行し、promptでOKを押すように指定する
choose ok on next confirmation

# promptに入力する
input text into prompt  プロンプトに入力しました

# OKボタンを押す
confirm action

# alertではpromptに入力した値が表示されているはずなので、一致しているかを確認する
${アラートメッセージ} =  get alert message
should be equal  ${アラートメッセージ}  プロンプトに入力しました

 

Headlessブラウザの制限

Headless Chrome v60やPhantomJSの場合、alertやpopup(acceptPopup, cancelPopup)などが未サポートです。
How to run Headless Chrome in Codeception - Codeception / Cookbook - PHP Test Club

そのため、stackoverflowにあるようにモンキーパッチすれば良いかもしれません。
selenium webdriver - chromedriver headless alerts - Stack Overflow

なお、Capybaraでは対応策があるようです。
RSpec + Capybaraでアラート/確認ダイアログを操作する場合は page.driver.browser.switch_to.alert.accept じゃなくて page.accept_confirm を使おう - Qiita

もっとも、Headless Chromeの今後のバージョンでは実装されるかもしれないため、今回はこの部分の対応方法は省略します。

 

ソースコード

GitHubに上げました。tests/の中にあるselenium_js_test.robotファイルが今回のテストファイルです。
thinkAmi-sandbox/RobotFramework-sample