Seleniumをtypescriptで使ってみる
画面開発でコーディングと動作確認を交互にしていると 毎回フル桁を入力して、ボタン押下して、結果確認して…の流れが結構辛い。
ということで、javascriptコードでブラウザ操作を自動実行してくれるseleniumを始めよう!(今更感)
seleniumインストール
何事もまずはnpm installから!!
npm install selenium-webdriver
次にseleniumが使うブラウザドライバーをダウンロードします
ダウンロードしたドライバーはプロジェクトのルートディレクトリに配置しておきます
TypeScriptでブラウザ操作コードを書く
シンプルなログイン画面にID、パスワードを入力してログインボタンを押下してみます
まずはtsファイルを作成してお作法コードを書きます インストールしたseleniumを使うよ!宣言とchromeの設定です
const webdriver = require('selenium-webdriver'); const { Builder, By, until } = webdriver; const assert = require('assert'); const capabilities = webdriver.Capabilities.chrome(); capabilities.set('chromeOptions', { args: [ '--headless', '--no-sandbox', '--disable-gpu', `--window-size=1980,1200` ] });
次にテキストボックスへの入力、ボタン押下、結果のassertを行います
/** * ログイン成功 */ (async () => { const driver = await new Builder().withCapabilities(capabilities).build(); await driver.get("http://localhost:3000"); // IDパスワードを入力 let userId = await driver.findElement(By.id('loginId')); userId.sendKeys("xxxxx"); let password = await driver.findElement(By.id('password')); password.sendKeys("xxxxx"); // ログイン let loginButton = await driver.findElement(By.id('loginButton')); loginButton.click(); await driver.wait(until.elementLocated(By.id('paymentList')), 10000); // 支払い一覧ページへ遷移していること assert.strictEqual(await driver.getCurrentUrl(), "http://localhost:3000/payment"); driver.quit(); })();
あらかじめ npm run serve
で開発サーバにアプリをホスティングしておき
node loginTest.ts
で上記コードを実行しましょう
自動でchromeが起動してログインを再現してくれます!!
Flutter インストールからメニュー表示まで
久しぶりにモバイルアプリを作ってみたくなったのでflutter始めました
iOS・Androidを同じコードで開発できるので効率的です
Flutterインストール
まずはhomebrewでFlutterをインストールしてdoctorコマンドでFlutterの利用開始チェックをする
(XcodeとAndroidStudioが入っていればチェックOKなはず)
brew install flutter flutter doctor
Flutterプロジェクト作成
インストール完了したらFlutterプロジェクトを作成します
--org
オプションでパッケージ名を指定できます(指定なしだと com.example
となる)
flutter create --org package project_name
プロジェクトが作成できたら標準出力に従ってアプリを起動!
cd project_name flutter run
プロジェクト作成直後の画面 カウントアップボタンのみがあるシンプルアプリ
画面をカスタマイズ
↑画面コードは project_name/lib/main.dart
に書かれているのでこちらを修正していく
まずはこんな感じで画面を修正しました
- アプリタイトルを変更
- AppBarの表示文字・カラーを変更
- サイドメニュー(Drawer)を追加
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'framboise nail', // ブラウザタブ表示名 theme: ThemeData(primarySwatch: Colors.blue), home: const MyHomePage(title: 'my page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), backgroundColor: Colors.pinkAccent.shade700, ), drawer: Drawer( // サイドメニュー child: ListView( children: <Widget>[ DrawerHeader( child: const Text( 'risa', style: TextStyle( fontSize: 24, color: Colors.white ), ), decoration: BoxDecoration(color: Colors.pinkAccent.shade700), ), const ListTile( title: Text('カタチから探す'), ), const ListTile( title: Text('スクエア'), trailing: Icon(Icons.square), ), const ListTile( title: Text('ラウンド'), trailing: Icon(Icons.circle), ), const ListTile( title: Text('オーバル'), trailing: Icon(Icons.egg), ), const ListTile( title: Text('ポイント'), trailing: Icon(Icons.water_drop), ) ], ), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: const <Widget>[ Text( 'You have pushed the button this many times:', ), ], ), ), ); } }
このようにプロジェクト作成直後から画面をカスタマイズできました!
サイドメニューは利用頻度が高そうなのでサラッとコード書けるようにしておきたい
ちょっとしたカスタマイズですが、何時間も掛かってしまいました…
webpack フロントエンドでも環境変数を扱う
React+SpringBootアプリでローカル、ステージング、本番環境用に環境変数ファイルがあって、それぞれに接続するAPIアドレスなどが定義されているのはよくあるケース
SpringBootで環境変数を読み込むのは何度も行ってきましたが、Reactで環境変数にアクセスしたことなかった!
今回フロントエンドはwebpack+Reactだったのでwebpackを使って環境変数を読み込む
そもそもwebpackの話
web資材(js、css、画像などのファイル)をpacking(まとめる)するツール
この記事のようにindex.jsなどのエントリポイントから参照される各モジュール(画面コンポーネントなど)をひとまとめにできる!
まとめる設定をwebpack.config.jsファイルに記述する
DefinePluginで環境変数を読み込む
あらかじめlocal.envなどの環境変数ファイルをサーバ内に環境変数として読み込まれるようにしておく
今回はdockerだったのでdocker-compose.ymlにビルド実行するコンテナのenv-filesにlocal.envを指定した
webpack.config.jsにDefinePluginを記述して環境変数をフロントエンドのグローバル変数に置き換える
local.envに定義したAPI_GATEWAYはprocess.env.でアクセスしグローバル変数API_HOSTに格納
const webpack = require('webpack'); module.exports = { mode: 'development', plugins: [ new webpack.DefinePlugin({ 'API_HOST': JSON.stringify(process.env.API_GATEWAY || "") }), ] };
実際にAPI接続するモジュールで declear const API_HOST: string;
と記述すると環境変数の値が利用できる
環境変数を扱う方法にはdotenv、dotenv-webpackもあるがライブラリを追加したくない場合はこの方法が使える
AWSメッセージキュー SQS
昨日はSNSがモバイル通知、分析用Firehorseなど複数の宛先へデータを流すことができると学んだので
今日はSQSの特徴を学んでいく!!(SESはEメール通知専用っていうのは知ってるからスキップ)
Amazon SQS
SNSのアイコンと比べると宛先は1つになる?中央の四角がキューを表している??
貯まったメッセージは受信側(Consumer)がキューに問い合わせを行うと、キューからメッセージが配信されて、受信側はメッセージを取得することが出来ます。
SNSはメッセージ発行元(publisher)から一方通行でデータが流れたけど
SQSはpublisherがキューにメッセージを入れて、consumerがポーリングでキューからメッセージを取り出すようだ
SQSを試してみる
マネジメントコンソールでSQS作成をしてみると、スタンダードとFIFOが選べる
配信順序を気にしなければスタンダードで良さそう(FIFOの方が割高)
スタンダードキューを作成してみたのでメッセージの送受信をクリックしてみた
メッセージ本文を入力してメッセージ送信をクリック!
メッセージがキューに送られたので利用可能なメッセージが1となった
自動でポーリングされないようだったのでメッセージをポーリングをクリックするとポーリングが開始された
メッセージが届きました!
SNSと連携
SQSのメニューにSNSサブスクライブとあったので昨日作成したSNSトピックを紐付けてみた
SNSで「わああああああ〜!」とメッセージを発行してみると
昨日作成したSMS通知がちゃんと届いたのと同時にSQSにもメッセージが渡ってきた!
SNSからSQSにメッセージを渡せる機能なのか!
EC2上のアプリからSNSで通知+SQSにデータを流してSQSから他のEC2アプリにデータを連携する時使えそう!
SQSはアプリ間連携に使えるメッセージキューということが分かった
AWSメッセージキュー SNS
AWS資格試験の模擬テストにメッセージキューが頻出するけど違いが分からない、覚えられないのでまとめておく。
AWS複数種類のメッセージキュー(MQ)が提供されているから1つずつ覚えていきます!
メッセージキューってなに??
メールやSMSメッセージ通知をする時にメッセージ作成アプリ→配信アプリに橋渡しをするキュー
メッセージキューって名前だけどメッセージ配信以外のアプリ間データ連携に利用されている
直接アプリ同士で連携してもいいけど、大量データだとアプリサーバに負荷が掛かっちゃうのでMQはよく使われるらしい
キューなので渡されたデータから処理されていく(先入先出)
Amazon SNSとは
サービスアイコンは左側から入ったデータがフィルタされて複数に流れるような
Amazon Simple Notification Serviceって名前の通り通知(SMS・プッシュ通知)によく使われる
SNSでスマホにメッセージ送信
トピック(後述)のタイプをスタンダードにするとSMS・プッシュ通知などのメッセージ通知ができ
FIFOにするとSQSのみ選択できた(こちらはアプリ間データ連携に使う時に選択するっぽい)
トピックを作成しなくても左メニューSMSを選択して電話番号を登録
ワンタイムパスワードで認証後、テストSMSが送信できる
今度はスタンダードタイプのトピックから送ってみる
トピックを選択してSMS送信をサブスクリプションに設定すると、こんなメッセージが届いた(一番下)
トピックから送信するとメッセージの先頭に「トピック名>」って付くみたい
アプリからSMS通知するには
携帯電話に発行する - Amazon Simple Notification Service
公式ドキュメントにコードから電話番号を動的に渡してAmazon SNSからSMS通知できるみたい
あらかじめトピックを作っておかなくてもSMSが送れるように見える
Amazon SNS トピックを作成する - Amazon Simple Notification Service
トピックは配信先のグループなので「メッセージ配信+配信内容を分析するRedshiftにデータ登録」を実現したい時に
トピックのサブスクリプションにSMS通知、kinesisFirehorseを登録してあげればトピックでメッセージ発行をすると
SMS通知が届き、一方ではFirehorse経由でRedshiftにデータが登録されました〜となる
Grid systemを思い出す
今ドキのレスポンシブな画面ではGridsystemが定番!だと思いますが、すぐに忘れちゃうので復習
html+bootstrapで簡単な検索フォームを作ってみます
<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>データ検索</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> </head> <body> <div class="container m-4"> <div class="row"> <div class="col"> <h4>データ検索</h4> </div> </div> </div> <div class="container m-4"> <div class="row justify-content-start"> <div class="col-md-2"> <h6>カテゴリ</h6> </div> <div class="col-md-2"> <h6>在庫</h6> </div> </div> <div class="row justify-content-start"> <div class="col-md-2"> <select class="form-select" aria-label=""> <option selected>飲み物</option> <option value="1">お菓子</option> <option value="2">雑貨</option> </select> </div> <div class="col-md-2 align-self-end"> <div class="form-check"> <input class="form-check-input" type="checkbox" value="" id="flexCheckDefault"> <label class="form-check-label" for="flexCheckDefault"> 在庫ありのみ </label> </div> </div> </div> <div class="row justify-content-start mt-2"> <div class="col-md-4"> <h6>商品名</h6> <input type="text" name="" value="" class="form-control"> </div> </div> <div class="row justify-content-start mt-2"> <div class="col-md-3"></div> <div class="col-md-1 text-end"> <button type="button" class="btn btn-outline-primary">検索</button> </div> </div> </div> </body> </html>
分かりづらいですが、紫がrow・黄色がcolです
最上部の項目、カテゴリと在庫をlabelとinputでrowを分けたのはチェックボックスを下寄せにするためです!
(rowを分けないと在庫のラベル表示が左のカテゴリのラベル表示とズレちゃいます)
検索ボタンの右寄せは空の col-md-3
分のdivを定義して無理やりぽいですが実現しています
(もうちょっとスマートな方法ないかな…?)
Webhookってなんだっけ??
先日RDS同士のデータ連携について調べたので打ち合わせで「調べました!」と報告すると「あとWebhookもありだよね」と言われました
あれ?Webhookってなんだったっけ…??
更新情報を他アプリにリアルタイム提供
GitHubにプッシュしたらCodePipelineが動き出す仕組み!JIRA更新した時のslack通知!
そういえば前にRedmine(EC2)でチケット更新したらslack通知する設定した記憶が…
あれがWebhookだったのか
RDS同期への応用方法
理想はこうなんだけど
RDSが更新されたらEventBridgeで更新を検知
更新されたテーブル名、CRUD区分、更新データをlambda引数に渡しもう一方のRDSを更新
EventBridgeでRDS更新は検知できなさそう??(RDSイベントになさそう)
EventBridgeからlambdaに更新したテーブル名とかデータとか渡せるんだろうか??
引数設定を入力トランスフォーマーにしたらいけるかもって思ったけど、入力トランスフォーマーに渡す値はイベントテキスト(定数json)だから無理かも…
時間ある時に苦戦してみるか笑