20210225のNode.jsに関する記事は5件です。

Node.js+MongoDB構成でGraphQLのお勉強

はじめに

現場でGraphQLを使用しているのですが、保守改修段階でプロジェクトに入ったのでスキーマ作成などの基本的なところをやったことがありませんでした。また、NoSQLデータベースも使ったことがなかったので、まとめて学んでみようということでNode.js+MongoDBの構成でGraphQLサーバをたててみることにしました。

今回作成したプロジェクトはGitHubにあります。

GraphQLのよさ

RESTとの比較記事がいたるところにあるので(例えばこちら)詳しく書きませんが、単一のエンドポイントであるというのがGraphQLの一番の特徴です。

GraphQLをつかうことで、RESTで必要なすべてのデータを取得しようとするときに発生する以下のような問題を解決することができます。

  • 複数のエンドポイントへリクエストを行う必要がある
  • 不要なデータも一緒に取得されてしまう

サーバ構築

適当なフォルダ(graphql-server-practice)を作成してyarn initします。そのあと、以下の構成でフォルダおよびファイルを作成します。

スクリーンショット 2021-02-24 20.50.27.png

Schema, Query, MutationをSchema.jsに記載していきます。

Schemaの作成

今回は練習のため、User, Hobby, Postの3つのSchemaをつくります。
Userはそれぞれ複数のHobbyやPostをもてるような関係になっています(One to Many relationship)。

まずはyarn add graphqlでgraphqlパッケージを導入し、schema.jsで以下を読み込みます。

schema.js
const graphql = require('graphql');

const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString,
  GraphQLInt,
  GraphQLSchema,
  GraphQLNonNull,
  GraphQLList,
} = graphql;

UserのSchemaをUserTypeとして作成します。GraphQLObjectTypeでラッピングすることでSchemaのname, description, fieldsを定義することができます。

fieldsのid, name, age, professionをGraphQLID, GraphQLString, GraphQLInt, GraphQLStringというスカラー型で定義します。

schema.js
const UserType = new GraphQLObjectType({
  name: 'User',
  description: 'Documentation for user...',
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    age: { type: GraphQLInt },
    profession: { type: GraphQLString },
  }),
});

HobbyTypeとPostTypeについても同様に作成します。

schema.js
const HobbyType = new GraphQLObjectType({
  name: 'Hobby',
  description: 'Hobby description',
  fields: () => ({
    id: { type: GraphQLID },
    title: { type: GraphQLString },
    description: { type: GraphQLString },
  }),
});

const PostType = new GraphQLObjectType({
  name: 'Post',
  description: 'Post description',
  fields: () => ({
    id: { type: GraphQLID },
    comment: { type: GraphQLString },
  }),
});

3つのSchemaを作成しましたが、この状態ではまだUserとPost、UserとHobbyの関係が定義されていないので、これらのリレーションを考えてあげる必要があります。

UserとPostの例を考えてみます。

const UserType = new GraphQLObjectType({
  name: 'User',
  description: 'Documentation for user...',
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    age: { type: GraphQLInt },
    profession: { type: GraphQLString },
    posts: {
      type: new GraphQLList(PostType),
      resolve(parent, args) {
        return postsData.filter((data) => data.userId === parent.id);
      },
    },
  }),
});

const PostType = new GraphQLObjectType({
  name: 'Post',
  description: 'Post description',
  fields: () => ({
    id: { type: GraphQLID },
    comment: { type: GraphQLString },
    user: {
      type: UserType,
      resolve(parent, args) {
        return usersData.find((data) => data.id === parent.userId);
      },
    },
  }),
});

User1つに対してPostは複数存在します。そのため、UserTypeのfieldsに新しく作られたpostsはPostTypeの配列型となり、new GraphQLList(PostType)と定義されます。

    posts: {
      type: new GraphQLList(PostType),
      resolve(parent, args) {
        return postsData.filter((data) => data.userId === parent.id);
      },
    },

また、resolveはどのUserに対するPostを表示するのかを定義するものです。parentは親のfields(ここではUserType)を指しており、以上の処理ではUserのidと等しいuserIdをもったPostのデータのみを取得するようになっています。

const usersData = [
  { id: '1', name: '山田勝己', age: 36, profession: 'SASUKE' },
];

const postsData = [
  { id: '1', comment: '僕にはSASUKEしかないんです', userId: '1' },
  { id: '2', comment: '完全制覇がしたいんです', userId: '1' },
];

Queryの作成

QueryはSchemaと同様に、GraphQLObjectTypeで定義を行います。試しに指定したidのUserを取得するuserクエリとすべてのUserを取得するusersクエリを作成してみます。

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  description: 'Description',
  fields: {
    user: {
      type: UserType,
      args: { id: { type: GraphQLID } },

      resolve(parent, args) {
        return usersData.find((data) => data.id === args.id);
      },
    },

    users: {
      type: new GraphQLList(UserType),
      resolve(parent, args) {
        return usersData;
      },
    },
  },
});

userクエリではidを引数(args)としてとるので、fieldsでargsの型定義を行っています。resolveではargsのidと同じデータだけ取得するような処理を書いています。

一方、usersクエリでは、すべてのデータを取得するだけなのでargsは必要ありません。

ローカルサーバをたててQueryの動作確認

Mutation作成とDB接続の前に、ローカルサーバをたててQueryの挙動を確認します。

作成したRootQueryをnew GraphQLSchemaでラッピングしてエクスポートします。

schema.js
module.exports = new GraphQLSchema({
  query: RootQuery
});

yarn add express express-graphqlで必要なパッケージを導入し、以下の設定を行います。

app.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');

const schema = require('./schema/schema');

const app = express();

app.use(
  '/graphql',
  graphqlHTTP({
    graphiql: true,
    schema,
  })
);

app.listen(4000, () => {
  console.log('Listening for requests on my awesome port 4000');
});

node appで4000ポートにサーバが立ち上がるのですが、ソースコードの修正をリアルタイムで反映させるためにyarn global add nodemonnodemonを導入します。

nodemon appでサーバを立ち上げ、http://localhost:4000/graphql を開くと以下の画面が現れます。

スクリーンショット 2021-02-25 8.04.42.png

userクエリを試しに実行すると右側に取得データが表示されます。postsのデータも問題なく表示されています。
スクリーンショット 2021-02-25 8.10.45.png

MongoDBとの接続

DBと接続してMutationを実装します。

MongoDB Atlasの設定

MongoDB Atlasのアカウントを作成します。Googleアカウントがあれば大丈夫です。
スクリーンショット 2021-02-25 21.26.37.png

適当な名前でProjectsを新規で作成します。
スクリーンショット 2021-02-25 21.29.32.png

Projects内でClusterを作成します。今回、クラウドにはAWSを使用し、DB性能に関わるCluster Tierには無料のM0 Sandboxを使用します。
スクリーンショット 2021-02-25 21.31.25.png

作成したClusterのCONNECTボタンを押して、"Connect using MongoDB Compass"を選択します。CompassはMongoDB用のGUIツールです。
スクリーンショット 2021-02-25 21.41.01.png

Compassをダウンロードし、DB接続用のコードをコピーします。
スクリーンショット 2021-02-25 21.43.06.png

Node.jsの設定

GraphQLとMongoDBと連携するために、Node.jsのmongooseというパッケージを使用します(yarn add mongoose)。
app.jsファイルを以下のようにします。mongoose.connectでMongoDBとの接続、mongoose.connection.onceで接続が成功したことを確認するためのコンソールログを行っています。

app.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const mongoose = require('mongoose');
const schema = require('./schema/schema');

const app = express();

mongoose.connect(
  'mongodb+srv://dbUser:<password>@cluster0.gjo5x.mongodb.net/test', // <password>には自分で設定したものを入力
  { useNewUrlParser: true }
);
mongoose.connection.once('open', () => {
  console.log('we are connected.');
});

app.use(
  '/graphql',
  graphqlHTTP({
    graphiql: true,
    schema,
  })
);

app.listen(4000, () => {
  console.log('Listening for requests on my awesome port 4000');
});

Modelの作成

DBのSchemaにあたるModelを作成していきます。GraphQLのSchemaとModelを関連付けることで、DBからデータを取得(Query)したり、登録・削除(Mutation)などを行うことができます。
modelフォルダ以下に新しいファイルを作成します。
スクリーンショット 2021-02-25 22.03.45.png

UserのModelは以下のようになります。userSchemaは後ほどschema.jsで読み込むので、最後にエクスポートします。

user.js
const mongoose = require('mongoose');
const MSchema = mongoose.Schema;

const userSchema = new MSchema({
  name: String,
  age: Number,
  profession: String,
});
module.exports = mongoose.model('User', userSchema);

Mutationの作成

Userデータの作成(CreateUser)、更新(UpdateUser)、削除(RemoveUser)のMutationsを作成します。

schema.js
const graphql = require('graphql');

const User = require('../model/user');

~中略~

const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    CreateUser: {
      type: UserType,
      args: {
        name: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: new GraphQLNonNull(GraphQLInt) },
        profession: { type: GraphQLString },
      },

      resolve(parent, args) {
        let user = new User({
          name: args.name,
          age: args.age,
          profession: args.profession,
        });

        return user.save();
      },
    },

    UpdateUser: {
      type: UserType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLString) },
        name: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: GraphQLInt },
        profession: { type: GraphQLString },
      },

      resolve(parent, args) {
        return (updatedUser = User.findByIdAndUpdate(
          args.id,
          {
            $set: {
              name: args.name,
              age: args.age,
              profession: args.profession,
            },
          },
          { new: true }
        ));
      },
    },

    RemoveUser: {
      type: UserType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLString) },
      },
      resolve(parent, args) {
        let removedUser = User.findByIdAndRemove(args.id).exec();

        if (!removedUser) {
          throw new 'Error'();
        }

        return removedUser;
      },
    },
}

~中略~

module.exports = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation,
});

resolve内でUserのModelを読み込み、データ登録用のメソッド(save)や更新用のメソッド(findByIdAndUpdate)を使用します。これらのメソッドについては、mongooseの公式Docsに使い方の詳細な説明が記載されています。

また、更新や削除では処理を行うデータを指定するため、argsのidはNon-nullとなります。Non-nullにしたいカラムについては、GraphQLNonNullをラッピングします。

最後にMutationのエクスポートも忘れずに行います。

Mutationの実行

localhost:4000/graphql を開き、CreateUserを試しに実行してみます。
スクリーンショット 2021-02-25 22.47.16.png

MongoDB Compassでデータが登録されたことを確認することができました。
スクリーンショット 2021-02-25 22.50.31.png

おわりに

今回のサーバ構築作業には結構時間がかかったのですが、AWS AppSyncを使ったら一瞬で構築できました。なかなか衝撃的な体験だったので、別記事で書こうと思います。
また、PostやHobbyのModelやMutationなど、記載を省略した部分についてご興味がありましたら、GitHubをご確認いただければと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでNode.jsアプリを起動する

概要

先日作ったSlackBotを定期実行したいため、Dockerでnodejsの環境を構築します。

前提条件

Dockerインストール済み

$ docker --version
Docker version 20.10.2, build 2291f61

ジョブフローを毎日18:00に実行する設定済み

job.js
const schedule = require('node-schedule');
schedule.scheduleJob(`00 00 18 * * 1,2,3,4,5`, run);

Dockerイメージを作成する

1、プロジェクトフォルダー直下にDockerfileを作成して、以下のコードを貼り付ける

FROM node:12.20.1
WORKDIR /app
COPY . .
RUN npm install
ENV TZ Asia/Tokyo
EXPOSE 8888
CMD ["node" , "job.js" ]

2、.dockerignoreファイルを設置してnode_modulesなどを転送対象から除外する

node_modules/
.gitignore
Dockerfile
package-lock.json

3、Dockerイメージのビルド

実行コマンド(magical-yuanxiaoはイメージ名)

$ docker build -t magical-yuanxiao .

ログ

Successfully built aebb1184bf44
Successfully tagged magical-yuanxiao:latest

コンテナを起動状態で作成する

実行コマンド(magical-yuanxiaoは先作ったイメージ)

$ docker run -p 8888:8888 magical-yuanxiao

最後に、docker ps -aコマンドでコンテナの稼働状況を確認します。

自動的に実行される

午後6時に自動投稿を行いました。
image.png
image.png

参照サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】日時処理を扱う方法

プログラミング勉強日記

2021年2月25日

日付処理を使うための準備

 Node.jsで日付処理を扱うために、今回はdate-utilsを使用する。
 date-utilsはnpmパッケージの1つで、簡単にインストールすることができる。

date-utilsをインストールする
$ npm install date-utils

現在時刻を表示する

// date-utilsを呼び出す
require('date-utils');
let now = new Date();
console.log(now.toFormat('YYYY年MM月DD日 HH24時MI分SS秒'));
console.log(now.toFormat('YY年M月D日 H時MI分SS秒'));
console.log(now.toFormat('DDD MMM DD YYYY HH24:MI:SS'));
console.log(now.toFormat('M/D/YY'));
実行結果
2021年02月25日 01時52分13秒
21年2月25日 1時52分13秒
Thu Feb 25 2021 01:52:13
2/25/21

参考文献

日付に関する実装!Node.jsで書くDateの使い方【初心者向け】

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js セキュリティアップデート + Node.js v15 について

Node.js セキュリティアップデートが出てた

https://nodejs.org/en/

2.23 に、Node.js の新しいバージョンがでたのでチェックしよう。
https://nodejs.org/en/blog/vulnerability/february-2021-security-releases/

10.x, 12.x, 14.x 15.x 用のセキュリティアップデート

重大(1), 高い深刻度(1), 低い深刻度(1)

  1. HTTP/2 の unknownProtocol があまりにも多くなった時、DoS 攻撃を受ける可能性がある問題
    • ファイルシステムが漏洩する可能性
    • メモリリークを引き起こす可能性
  2. localhost6 がホワイトリストに含まれている場合、DoS 攻撃を受ける可能性がある問題
  3. OpenSSL の脆弱性が Node.js を介して悪用される可能性がある問題

適宜追従しましょう(フロントエンドだけなら別にって感じだけど)。

Nodejs 10.x は 2021.4.30 にサポート終了予定、12.x 系は 2022.4.30 で終了予定
※奇数バージョンは機能開発用バージョンで、偶数バージョンは長期サポートバージョン。最新 15.x 以外の奇数はない

Node.js のバグ報告について

nodejs-sec グループ
https://groups.google.com/g/nodejs-sec?pli=1

バグ報告の方法
https://github.com/nodejs/node/blob/master/SECURITY.md

Nodejs にバグを報告するとお金もらえるプログラム:バグバウンティプログラム
https://hackerone.com/nodejs


Node.js v15 について

Node.js v15 あまりキャッチアップしてなかったので、おさらいしてみました。

https://nodejs.org/en/

v15 のリリースは 2020.10.21。v16 は 2021.4 とかですかね。

公式 Medium
https://nodejs.medium.com/node-js-v15-0-0-is-here-deb00750f278

codedamn さんのまとめ YouTube
https://www.youtube.com/watch?v=OIzGI5wFiXg

めっちゃまとまってる
https://shisama.hatenablog.com/entry/2020/10/21/004612

1. npm 7 が同梱

npm 7 が入った。

https://www.npmjs.com/package/npm

  • workspace という概念が入った
    • monorepo で作りたい時とかは便利そう
  • acceptDependencies という概念も入った
  • peerDependencies の挙動変更
  • npx が実行前に確認されるようになった
  • npm audit の表示方法が変更
  • package.exports が npm の内部モジュールを参照できないようになった
  • npm test の表示内容が変更
  • npm buildnpm unbuild が廃止

https://blog.watilde.com/2020/10/14/npm-v7の主な変更点まとめ/

2. unhandledRejection の挙動変更

ブレイキングチェンジ!

  • unhandledRejection の挙動が変わった。default warn だったのが throw になった。

ただそもそもこれが起きてる時は以下のタイミングで

  • Promise を使った非同期処理において想定外のエラーが起きた
  • エラーハンドリングが漏れている
  • 単純なコードのエラー

マズイプログラムになっているはずなので、挙動が変更になることでより安全なコードが書けるようになった。

https://zenn.dev/kimamula/articles/b32d11d52c2b7a733119

unhandledRejection について

https://medium.com/@hagevvashi/unhandledrejectionについて-70739d2b6a60

Web サービスを運営していると、Sentry を使ってエラーハンドリングしたりすると思う

https://sentry.io/welcome/

window.onerror は、Promise.reject() によって投げられたエラーは拾わない
(ただし console.error() 形式で表示はされる)

Promise を使うエラー(Node.js でファイル操作だとかをするときの I/O 処理や、ブラウザで API 通信の失敗)を検知する時に使う。

try {
  await axios.post("/some-resources");
} catch(e) {
  // error handling
  throw e; // this causes unhandled rejection
}
window.addEventListener("unhandledrejection", (e: PromiseRejectionEvent): void => {
  // an error loginng
  logError(e.reason);
});
process.on("unhandledRejection", (error: {} | null | undefined, promise: Promise<any>) => {
  // an error loging
  logError(error);
})

https://medium.com/@hagevvashi/unhandledrejectionについて-70739d2b6a60

3. ES2021 に対応

JavaScript エンジンの V8 が v8.6 にアップデート。まぁでもブラウザでは既に使えるので珍しくはない

Promise.any

Promise.all は、全部が resolved になった時 / どれかが rejected になった時エラー

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'foo');
});

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values);
  })
  .catch(() => {
    console.log("error");
  });
// expected output: Array [3, 42, "foo"]

Promise.race は、どれかが resolved になった時 / どれかが rejected になった時エラー
Promise.allSettled は、全部が resolvedrejected になった時 / エラーにはならない
Promise.any は、どれかが resolved になった時 / 全部が rejected になった時エラー

10 個の Promise を走らせて、9 個失敗しても、最後の 1 つが成功なら成功としたい時(race だとどれかが reject になるとエラーになっちゃう)に使う

WeakRefs

Garbage Collection 時に破棄できるオブジェクトを作れる。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef

var ref = new WeakRef(element)
ref.deref() // 参照元を取り出す

簡単にサンプルを作ってみた。↓
https://playcode.io/740665/

タイマーが動き続けている。別のタイマーで DOM 要素を 5 秒後に remove する。元のタイマー関数では参照はできている。Garbage Collection されると参照がきえて止まる。

メモリリークを防ぐコードが書ける。

AggregateError

複数のエラーをまとめたい時に使う。

try {
  throw new AggregateError([
    new Error("some error"),
  ], 'Hello');
} catch (e) {
  console.log(e instanceof AggregateError); // true
  console.log(e.message);                   // "Hello"
  console.log(e.name);                      // "AggregateError"
  console.log(e.errors);                    // [ Error: "some error" ]
}

エラーを配列で与えられる。

Logical Assignment Operators 論理代入演算子

読みにくくなるからやめてほしいw

let a = true
a &&= false // x && (x = y): x と y が true な値の場合、y の値が代入される
// false
a
// false
a ||= true // x || (x = y): x が false な値の場合、y の値が代入される
// true
a
// true
a = null
a ??= 1 // x ?? (x = y): x が null or undefined な値の場合、y の値が代入される
// 1
a
// 1
a ??= 2
// 1
a
// 1

Numeric separators

桁が読みやすくなるから積極的に使おう。

123 === 1_2_3
// true
1_000_000_000.000_001
// 1000000000.000001

ただこんなでかいマジックナンバー扱いたくないし扱う機会もない気がする。

String.prototype.replaceAll

ブラウザで使えるようになってたやつ。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll
https://caniuse.com/mdn-javascript_builtins_string_replaceall

IE 11 は Babel を通せば replaceAll が書かれたコードはトランスパイルしてくれる。preset-env (core-js) を使うことで IE11 でも動く。

https://v8.dev/features/string-replaceall

ちなみに Next.js は @babel/preset-env 入ってないので IE11 対応させるなら config で指定する。

4. Web Crypto API

JavaScript で暗号化・復号・署名・検証ができる Web 標準 API。

https://caniuse.com/cryptography

ブラウザでは昔っから使えたやつ。
Node は最近ブラウザで使えたやつが使えるようになってきているといういい話。

5. AbortController

Web リクエストを中止することができるグローバルユーティリティオブジェクトの AbortController が Node.js 15 でも使えるようになった。

const ac = new AbortController();
ac.signal.addEventListener('abort', () => console.log('Aborted!'),
{ once: true });
ac.abort();
console.log(ac.signal.aborted);  // Prints True

動画のダウンロードを中止する例

https://developer.mozilla.org/en-US/docs/Web/API/AbortController

var controller = new AbortController();
var signal = controller.signal;

var downloadBtn = document.querySelector('.download');
var abortBtn = document.querySelector('.abort');

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', function() {
  controller.abort();
  console.log('Download aborted');
});

function fetchVideo() {
  ...
  fetch(url, {signal}).then(function(response) {
    ...
  }).catch(function(e) {
    reports.textContent = 'Download error: ' + e.message;
  })
}

fetch はまだ Node.js に実装されてないけどね。

フロントエンドの UI 開発で、ボタンに対しての mousedown/mouseup イベントを addEventListen していて、クリック後画面遷移してしまうので removeEventListen しておきたい時、abortController を使えば一発で解除できるテクがどっかで紹介された。

6. Event Target

ブラウザで使えた EventTarget が Node.js でも使えるようになった。Event の発行・伝播はブラウザでは EventTarget、Node.js では EventEmitter を使っていた。

https://nodejs.org/api/events.html#events_eventtarget_and_event_api

const target = new EventTarget();

target.addEventListener("foo", (event) => {
  console.log("foo is called");
});

const ev = new Event("foo");
target.dispatchEvent(ev);

イベントが DOM ツリーを伝って伝播が行われないなど差はあるが、ユニバーサルなプログラムが書けるようになってきている。

7. N-API updated

Node.js のネイティブ拡張をするための Node API (通称 N-API) がアップデート。C 言語のインターフェース。まぁ使うことはあまりないのかなぁ。

https://nodejs.org/api/n-api.html#n_api_node_api

8. QUIC (実験的機能)

UDP ベースの通信プロトコル「QUIC」が使用できるようになった。
TLS 1.3 ベースでのセキュリティ、フロー制御、エラー訂正、接続、多重化とかとか。

TLS 1.3 とは: https://kinsta.com/jp/blog/tls-1-3/

HTTP/3 サーバーを作ることができる。
https://blog.leko.jp/post/http-over-quic-on-nodejs15/

むずすぎてよくわからんですね

かんそう

  • せめて Node.js 14.x にはしとこう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Puppeteerでドラッグ&ドロップ

はじめに

最近、フロントテストにPuppeteerというライブラリを用いているのですが、ドラッグ・ドロップの動作においてかなり苦戦をしたので、これ以上犠牲者を増やさないために()、ここに解決策を共有したいと思います。

Puppeteerでドラッグ&ドロップができない!

「Puppeteer drag drop」というように検索をかけると、大体下記のようなコードが出てきます。

    await page.mouse.move(x1, y1);
    await page.mouse.down();
    await page.mouse.move(x2, y2);
    await page.mouse.up();

座標(x1, y1)にカーソルを合わせ、掴む→(x2, y2)に移動し離すという意味のコードです。

しかしこれ...

動作しません!!!!!

正確に言うと、一瞬だけ対象物が動くんですけど、(x2, y2)まで移動してくれないんですよね...
調べてみると、このことに関して困っている方が、国内外問わず結構いました。

await page.mouse.up();
この処理がうまく動作していないのが原因だと思っているが、どうなんだろう。
処理自体にエラーはなく、nodeも正常に終了しています。
しかし私の環境ではテスト終了後、エディタにカーソルをもっていくと常に何かをドラッグしている挙動が発生します。

(Puppeteerで困っていることより引用)

解決策

解決策は、「mouse.moveにoptionsとしてstepsを適切に設定してあげる」です。
公式ドキュメントには

steps <number> defaults to 1. Sends intermediate mousemove events.

と書かれています。
今回はこのstepsを20と設定します。
すなわち、先ほどのコードを

    await page.mouse.move(x1, y1);
    await page.mouse.down();
    await page.mouse.move(x2, y2,{steps: 20});//ここにoptionsとして{steps: 20}を追加した。
    await page.mouse.up();

のように書き換えると、期待通り動作します。

参考

・上記に引用として示したもの
How to simulate Drag-Drop action in pupeteer

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む