僕は技術ができない

技術のできないスーツ園児ニアが考えていること

Angular で Firebase の Database, Storage データを読み取るまでの流れ

前回は Angular プロジェクトを Firebase にデプロイし、アクセスするまでをやりました。

www.contemporarycuz.com

今回は、Firestore や Storage につないでデータを表示するところまでをやります。

ケースとしては、とりあえずアプリをデプロイして繋ぐことができればOKというガバガバ仕様を想定してますので予めご了承ください。

Firebase 側での作業

Config の取得

Firebaseコンソールの「Project Overview」から、</> (Webアプリ)を選択し、アプリを登録します。

f:id:contemporarycuz:20190917170145p:plain

f:id:contemporarycuz:20190917170452p:plain

SDK Snippet からアプリのConfig類をコピーしておきます。

f:id:contemporarycuz:20190917171050p:plain

Firestore へのデータ登録

コレクションを作成します。

f:id:contemporarycuz:20190917175143p:plain

今回は以下の設定で作成しました。

  • コレクション ID:hoge-collection
  • ドキュメント ID:hoge-doc

こんな感じです。

f:id:contemporarycuz:20190917175356p:plain

Firestorage へのデータ登録

適当にファイルをアップロードします。

f:id:contemporarycuz:20190917181642p:plain

アクセス権をガバガバにします。以下のように読み取りアクセスをパブリックに向けます。

f:id:contemporarycuz:20190917191852p:plain

とりあえず、Firebase側での作業はこれで終わりです。

ローカル側での作業

アプリをFBのリソースを利用するように書き換えます。

Firebase モジュールのインポート

@angular/fire パッケージをインストール(npm リファレンス

$ npm install --save firebase @angular/fire

src/app/app.module.ts を書き換えます。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { AngularFireModule } from '@angular/fire';  // 追記
import { AngularFireAuthModule } from '@angular/fire/auth';  // 追記
import { AngularFirestoreModule } from '@angular/fire/firestore';  // 追記
import { AngularFireStorageModule } from '@angular/fire/storage';  // 追記

var firebaseConfig = {
  apiKey: "(略)",
  authDomain: "(略)",
  databaseURL: "(略)",
  projectId: "(略)",
  storageBucket: "(略)",
  messagingSenderId: "(略)",
  appId: "(略)"
};

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(firebaseConfig),  // 追記
    AngularFireAuthModule,  // 追記
    AngularFirestoreModule,  // 追記
    AngularFireStorageModule  // 追記
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

テスト用にコンポーネントを用意

コンポーネントを生成します。

$ ng generate component firestore
$ ng generate component firestorage

ルーティング設定をします。

src/app/app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { FirestoreComponent } from './firestore/firestore.component'; // FirestoreComponentをimport
import { FirestorageComponent } from './firestorage/firestorage.component'; // FirestorageComponentをimport


const routes: Routes = [
  {path: 'firestore', component: FirestoreComponent}, // FirestorageComponentを追加
  {path: 'firestorage', component: FirestorageComponent} // FirestoreComponentを追加
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

デフォルトのトップページが邪魔なので消します。

src/app/app.component.html

<router-outlet></router-outlet>

疎通確認します。

$ ng serve --open

f:id:contemporarycuz:20190917173536p:plain

Firestore コンポーネントの書き換え

適当に書き換えます。このdataにFirestoreに登録したデータを表示させます。 src/app/firestore/firestore.component.html

<div>
    <p>firestore works!</p>
</div>
<div>
    <p>{{data}}</p>
</div>

src/app/firestore/firestore.component.ts

import { Component, OnInit } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore'; // @angular/fire/firestore のインポート

@Component({
  selector: 'app-firestore',
  templateUrl: './firestore.component.html',
  styleUrls: ['./firestore.component.css']
})
export class FirestoreComponent implements OnInit {
  data: string = 'this variable is from firestore'; // 変数の初期化

  constructor(private firestore:AngularFirestore) { }

  // データベースの読み取り(hoge-collection -> hoge-doc -> hoge-field)
  ngOnInit() {
    this.firestore.collection('hoge-collection').doc('hoge-doc').valueChanges().subscribe((value)=>{
      this.data = value['hoge-field'];
    });
  }

}

疎通確認します。

f:id:contemporarycuz:20190917175956p:plain

Firestoreの値を書き換え、リアルタイムに画面も更新されるか見てみましょう。

f:id:contemporarycuz:20190917180202p:plain

Firestorage コンポーネントの書き換え

src/app/firestorage/firestorage.component.html

<div>
    <p>firestorage works!</p>
</div>
<div>
    <img id="image" src="" />
</div>

src/app/firestorage/firestorage.component.ts

import { Component, OnInit } from '@angular/core';
import { AngularFireStorage } from '@angular/fire/storage';

@Component({
  selector: 'app-firestorage',
  templateUrl: './firestorage.component.html',
  styleUrls: ['./firestorage.component.css']
})
export class FirestorageComponent implements OnInit {

  constructor(private storage:AngularFireStorage) {  }

  ngOnInit() {
    this.storage.storage.ref('angular.png').getDownloadURL().then((url) => {
      var img = (<HTMLInputElement>document.getElementById('image'));
      img.setAttribute('src', url);
    });
  }
}

(参考)TypescriptでHTMLElementを使う方法

stackoverflow.com

TypescriptもAngularもわからないマンなので document.getElementById を使わずに Angular で正しく実装する方法がよくわからず、、、 だれか教えてください。

疎通確認します。

$ ng serve --open

f:id:contemporarycuz:20190917212939p:plain

Firebase にデプロイ

ビルド&デプロイ

$ ng build --prod
$ firebase deploy

デプロイ先で疎通確認

f:id:contemporarycuz:20190917214418p:plain

Angular プロジェクトを Firebaseにデプロイするまでの流れ

今回は Angular プロジェクトを Firebase にデプロイし、アクセスするまでの流れをまとめます。

ケースとしては、とりあえずアプリをデプロイして繋ぐことができればOKというガバガバ仕様を想定してますので予めご了承ください。

Firebase側での作業

プロジェクトの作成

以下にアクセス
https://console.firebase.google.com/

Firebase アカウントを作成していない場合は作成してください。特記すべきことは特になく、案内に従って適当にポチポチしてればできます。

以下の設定値でプロジェクトを新規作成します。

  • プロジェクト名:deploy-test
  • Google Analytics の設定:デフォルトのFBアカウント

Authentication の設定

プロジェクトが作成されたら、左ペインから「Authentication」をクリックします。 「ログイン方法」タブから適当にログインプロバイダを選び、設定します。 今回は Google アカウントでログインするように設定します。

f:id:contemporarycuz:20190917160408p:plain

「プロジェクトの公開名」と「プロジェクトのサポートメール」を設定し、「有効にする」トグルを有効にし、「保存」をクリックします。

f:id:contemporarycuz:20190917160843p:plain

Database の設定

プロジェクトホームの左ペインから「Database」をクリックし「データベースの作成」をクリックします。

f:id:contemporarycuz:20190917161229p:plain

以下の設定値で Firestore を作成しました。セキュリティルールは個々の事情に合わせて選んでください。

  • セキュリティルール:テストモード
  • ロケーション:asia-northeast1

Storage の設定

プロジェクトホームの左ペインから「Storage」をクリックし、「スタートガイド」をクリックします。

f:id:contemporarycuz:20190917161958p:plain

セキュリティ保護ルールとロケーションはデフォルトのまま作成します。

一旦これで FIrebase 側の作業は終了です。

ローカル側での作業

Angular プロジェクトの作成〜ビルド

$ ng new deploy-test
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? (Use arrow keys)
❯ CSS 
  SCSS   [ https://sass-lang.com/documentation/syntax#scss                ] 
  Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ] 
  Less   [ http://lesscss.org                                             ] 
  Stylus [ http://stylus-lang.com                                         ]

プロジェクト直下に移動し、疎通確認します。

$ cd deploy-test
$ ng serve --open

一旦ビルドします。

$ ng build --prod

ツール類 のインストール(まだしていない場合)

Angular CLI

以下を実行します。

$ npm install -g @angular/cli --unsafe-perm
$ ng --version
Angular CLI: 8.3.4

Firebase Tools

以下を実行します。

$ npm install -g firebase-tools --unsafe-perm
$ firebase --version
7.3.2

Firebase へのデプロイ設定

以下を実行します。 今回は「Firestore」「Hosting」「Storage」を利用するので、それらにチェックを入れています。

$ firebase login
$ firebase init

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/(...略...)/deploy-test

? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. 
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◉ Firestore: Deploy rules and create indexes for Firestore
 ◯ Functions: Configure and deploy Cloud Functions
 ◉ Hosting: Configure and deploy Firebase Hosting sites
❯◉ Storage: Deploy Cloud Storage security rules

プロジェクトセットアップでは、Firebaseの設定ファイルを指定します。 ここでは自動生成するために基本的にはデフォルト通りで設定しています。 ただし、注1〜注3では、デフォルトのままだと勝手に index.html を生成してしまうので、ビルドしたファイルの index.html に向くように設定します。 ここでは、「dist/deploy-test/index.html」を向くように設定し、overwriteされないようにしています。

=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

? Please select an option: Use an existing project
? Select a default Firebase project for this directory: deploy-test-(略) (deploy-test)
i  Using project deploy-test-(略) (deploy-test)

=== Firestore Setup

Firestore Security Rules allow you to define how and when to allow
requests. You can keep these rules in your project directory
and publish them with firebase deploy.

? What file should be used for Firestore Rules? firestore.rules

Firestore indexes allow you to perform complex queries while
maintaining performance that scales with the size of the result
set. You can keep index definitions in your project directory
and publish them with firebase deploy.

? What file should be used for Firestore indexes? firestore.indexes.json

=== Hosting Setup

Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory? dist/deploy-test  // ☆注1☆
? Configure as a single-page app (rewrite all urls to /index.html)? Yes  // ☆注2☆
? File dist/deploy-test/index.html already exists. Overwrite? No  // ☆注3☆
i  Skipping write of dist/deploy-test/index.html

=== Storage Setup

Firebase Storage Security Rules allow you to define how and when to allow
uploads and downloads. You can keep these rules in your project directory
and publish them with firebase deploy.

? What file should be used for Storage Rules? storage.rules

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!

Firebase へのデプロイ

$ firebase deploy

=== Deploying to 'deploy-test-(略)'...

i  deploying storage, firestore, hosting

(...略...)

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/deploy-test-()/overview
Hosting URL: https://deploy-test-().firebaseapp.com

Hosting URLにアクセスします。

f:id:contemporarycuz:20190917164546p:plain

これでデプロイまで行けました。

Angularでプロジェクトの作成からページ遷移の実装まで

最近 Angular をはじめました。

プロジェクトを作成してからページ追加、ページ遷移の実装までの流れを一旦整理しておきたいと思います。

実装

プロジェクトの作成

適当にプロジェクトを作ってプロジェクト直下に潜ります。
1つ目の質問に Yes と答えています。これするとページ遷移の実装に必要な app-routing.module.ts が作られます。
2つ目の質問はお好みで。

$ ng new my-router-app
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? (Use arrow keys)
❯ CSS 
  SCSS   [ https://sass-lang.com/documentation/syntax#scss                ] 
  Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ] 
  Less   [ http://lesscss.org                                             ] 
  Stylus [ http://stylus-lang.com                                         ] 

... 略 ...

$ cd my-router-app

コンポーネントの作成

コンポーネントを作っていきます。

$ ng generate component main
$ ng generate component page1

ルーティング設定

app-routing.module.ts にルーティング設定をします。

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MainComponent } from './main/main.component'; // MainComponentをimport
import { Page1Component } from './page1/page1.component'; // Page1Componentをimport

const routes: Routes = [
  {path: '', component: MainComponent}, // MainComponentを追加
  {path: 'page1', component: Page1Component} // Page1Componentを追加
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

ng generate component をした時点で app.module.ts に以下のようにページが追記されていると思いますが、一応確認します。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MainComponent } from './main/main.component';
import { Page1Component } from './page1/page1.component';

@NgModule({
  declarations: [
    AppComponent,
    MainComponent, // MainComponent
    Page1Component // Page1Component
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

デフォルトのサンプルページを消す

デフォルトで作られるサンプルページが邪魔なので、消します。

<router-outlet></router-outlet> // これだけ残す

疎通確認

ローカルでServe

$ ng serve

rootのページ f:id:contemporarycuz:20190915135428p:plain

Page1のページ f:id:contemporarycuz:20190915135508p:plain

ページ追加ができました。

Docker Registry と Repository の違いをめちゃくちゃわかりやすく説明してみた

Docker には Docker Registory(以下 レジストリ)と Repository(以下リポジトリ)の概念があります。

「レジストリ」のことを「リポジトリ」って普段あまり意識せずに使ったりしますが、全然違うものなのでしっかりと区別して理解しておきたいです。

まだ僕は Docker 初心者なので間違ってるかもしれません。コメントください。


目次

まずはふわっと理解

まず、 レジストリ は「コンテナイメージをホストするサーバ」のことを指します。Docker イメージを保管しておく場所です。

そして、 リポジトリ は「同じイメージ名だけど、異なるタグを持つイメージの集合」のことを指します。

以下では「リポジトリは機能的に似通ったイメージのコレクションで、同じ名前を持つが異なるバージョンがあって、タグによって個々のイメージは区別できる」と言及されています。

Repository is a collection of the functionally similar images, which share same name but may represent different versions/flavours of the same software. Images in the repository are distinguished by tags.

What is the difference between a Docker repository and an image?


絵に描いてみるとこんな感じです。

f:id:contemporarycuz:20190830010306p:plain

イメージ名に対応するリポジトリがあり、それらをレジストリがホストしているという構造になります。

実際にはレジストリとして Docker Hub やプライベートレジストリを利用するため、
<リポジトリ名> <レジストリホスト名>/<イメージ名>
となります。

例えば、my-private-registry/my-image とか。


手を動かして確認

ふわっと理解したら、実際に手を動かして確認してみます。

ローカルに持っているイメージにタグをつけていきます。 ここでは Azure Container Registry を利用する感じで書きますが、本筋には関係ないはずです。

# とりあえずタグをつける
$ docker tag my-local-image my-private-registry.azurecr.io/my-image:1.0.0

# プッシュするイメージ名は "同じ" で タグを変える
$ docker tag my-local-image my-private-registry.azurecr.io/my-image:1.1.0

# イメージ名が異なるリポジトリ名をつける
$ docker tag my-local-image my-private-registry.azurecr.io/my-image-another:1.2.0

次にリモートリポジトリにプッシュします。

$ docker push my-private-registry.azurecr.io/my-image:1.0.0

$ docker push my-private-registry.azurecr.io/my-image:1.1.0

$ docker push my-private-registry.azurecr.io/my-image-another:1.2.0

レジストリ 内の リポジトリ 一覧を取得します。

$ az acr repository list --name my-private-registry --output table

Result
------------------------
my-image
my-image-another

3つのイメージをプッシュしましたが、その内2つのイメージ名が同じなので、結果的にリポジトリは2つになっています。

最後にそれぞれのリポジトリのタグ一覧を確認してみます。

# リポジトリ "my-private-registry.azurecr.io/my-image" のタグ一覧を取得
$ az acr repository show-tags --name my-private-registry --repository my-image --output table

Result
--------
1.0.0
1.1.0

# リポジトリ "my-private-registry.azurecr.io/my-image-another" のタグ一覧を取得
$ az acr repository show-tags --name my-private-registry --repository my-image-another --output table

Result
--------
1.2.0

人と話をするときには ちゃんと レジストリ と リポジトリ を使い分けて話そうと思います。

Kubernetes の API Resource をふわっと理解する

Kubernetes上で扱われる情報(Pod, Service等)は api-server 上では API Resource として定義されています。
それに対して、実際にKubernetes上で動作しているリソース(というかプロセスと言ったほうがいいかもしれない)のことをオブジェクトと言います、たぶん。

API Resource には大きく5種類があります。
Kubernetes API Reference v1.15

リソースカテゴリ 概要
Workloads クラスタでコンテナを管理、実行するために使用するオブジェクト
Discovery & LB Podを外部からアクセス可能なロードバランサーにつなぐために使用するオブジェクト
Config & Storage アプリケーションに設定値を埋め込む、データをコンテナの外部で永続化するために利用するオブジェクト
Cluster クラスタ自体の構成方法を定義するオブジェクト
Metadata クラスタ内の他のリソースのふるまいを構成するオブジェクト

上記の定義でいまいちわからないのが Cluster と Metadata。

例を考えてみるとイメージはつくかな。

Cluster Resources のふわっとしたイメージ

Cluster Resources は Node, Namespace, ServiceAccountといったクラスタ自体の管理に必要なリソースのこと。

  • Node は コンテナをホストしてる物理的なマシンのこと(VMでもいいけど)。
  • Namespace は Kubernetes クラスタ内を論理的に分ける単位。Linux の Namespace とは別だよ。
  • ServiceAccount は クラスタ内のなんらかのプロセスが api-server にリクエスト投げるときに使うアカウント。api-server には認証認可の仕組みがあるのでリクエスト投げるならアカウントが必要になる。

Metadata Resources のふわっとしたイメージ

Metadata Resources は LimitRange, HorizontalPodAutoscaler といった、オブジェクトの制限やスケール、管理に関するリソースのこと。

  • LimitRange は性能の上限と下限を設定するリソース
  • HorizontalPodAutoscaler は Pod のレプリカ数をCPU負荷によってスケールさせるリソース

リソース(オブジェクト)の全体感はこんな感じで。

Mapping Data Flow(Azure Data Factory)使ってみた

だいぶ前になりますが、Azureもくもく会でLTさせていただいたので資料を共有します。
ADF Pipelineの中でデータ加工を簡単にGUIポチポチ操作だけでできる便利な機能です。
プレビュー機能なので本番ではまだまだ使えないとは思いますが、GAされたらすぐ使えるように今後もちょくちょく触っていこうと思います。

speakerdeck.com

Azure Functions for Node で単体テストする方法(園児向け)

今回は Azure Functions for Node で 単体テスト(Unit Test)するやり方を紹介します。
とはいえ、これってガッツリ公式ドキュメントに載っているので、MSのドキュメント自力で完璧に読めるぜ!って方はお帰りください。

docs.microsoft.com

MSのドキュメント色んな意味でムズ過ぎて読めないって人には役に立つかもです。

はじめに

  • 基本的には公式ドキュメントの通りです。
  • ただ、一部妙にわかりにくい部分があるので自分へのメモを兼ねて残しておきます。わかりにくい部分に関しては本記事の最後に書いています。
  • 公式ドキュメントとはフォルダ構成を少し変えています(gitignoreで管理しやすくするためテストに関するものはすべて外出ししている等)
  • フツーのAzure Functions (for Node)のUnit Test方法の紹介です。Durable Functions (for Node)のためのテスト方法ではありません。Jestの詳細な使い方にも触れていません。
  • ひょっとしたら、頑張ればDurable Functionsでもいけるかもしれません(いけないかもしれません)

Jest

Azure Functions for Node の Unit Test には Jest を使います。
JestというのはJavaScriptのためのテストツールです。Wikipediaでは以下のように定義されています。

Jest[1] is a JavaScript Testing Framework with a focus on simplicity. It works with projects using: Babel, TypeScript[2], Node.js, React, Angular and Vue.js. It aims to work out of the box and config free.
Jest (JavaScript framework)

公式ページはなんだか楽しそう。 f:id:contemporarycuz:20190716232313g:plain jestjs.io

本題(Functions向けのJest導入)

HttpTriggerタイプのFunction Appを用意

こんな感じ

> tree /f
C:...\UNITTEST
│  .gitignore
│  host.json
│  local.settings.json
│  proxies.json
│
├─.vscode
│      extensions.json
│      launch.json
│      settings.json
│      tasks.json
│
└─HttpTrigger
        function.json
        index.js
        sample.dat
上記UnitTestフォルダ直下で以下を実行
mkdir TestModule        // 名前はテキトー
cd TestModule
npm init -y                   // package.json作るため脳死実行
npm i jest                     // Jestインストール
package.jsonのscripts.testをjestに変更
  "scripts": {
    "test": "jest"
  },
TestModule直下で以下を実行
mkdir testing
cd testing
ni defaultContext.js        // Powershellを想定。niはtouchみたいなやつ。
ni index.test.js
defaultContext.jsに以下をコピペ
module.exports = {
    log: jest.fn()
};
index.test.jsに以下をコピペ(こいつがテストコードです)
const httpFunction = require('../../HttpTrigger/index');
const context = require('./defaultContext');

test('Http trigger should return known text', async () => {

    const request = {
        query: { name: 'Bill' }
    };

    await httpFunction(context, request);

    expect(context.log.mock.calls.length).toBe(1);
    expect(context.res.body).toEqual('Hello Bill');
});

これでテストの準備完了!!
この時点でTestModule内はこんな感じになってるはず。

│  package-lock.json
│  package.json
└─testing
        defaultContext.js
        index.test.js

テスト実行

npm test

f:id:contemporarycuz:20190716234932p:plain

無事、テスト実行することができました。

次回は一歩進んで、入出力Bindingのモック作成あたりに触れようかなと。

一部妙にわかりにくい部分について

その①:HttpTriggerとTimerTriggerのテストがごっちゃになって書かれている。

以下の手順の時点では
「HttpTriggerとTimerTriggerのFunctionに対するテストを実施します」
とも言われていないし、
そもそもFunctionすら作っていません。
今何をしようとしているのか全く見えないという状況に陥ります。 f:id:contemporarycuz:20190717000119p:plain

実はここら辺のことはC#を用いた単体テストの章に書かれていたりします。しかし、Nodeで開発している人はその部分は読まないでしょう。

f:id:contemporarycuz:20190717000435p:plain

その②:ワタシニホンゴワカリマセン的な珍訳

これはMSドキュメントあるあるですが、日本語が意味不明です。 f:id:contemporarycuz:20190717000655p:plain

関数を作成した後、index.test.js という名前の同じフォルダーに新しいファイルを追加

これだけ読むと、「index.test.jsという名前のフォルダ―」に「index.test.jsというファイルを追加する」ように読めてしまいます。
もちろん、優秀な皆さんであれば、コードを見て

const httpFunction = require('./index');

の部分から、どこにindex.test.jsを作るべきかわかるのだと思います。しかし、園児にはちょっと厳しいです。
ちなみに、英語版を読んでもちょっと混乱します(これは僕の英語力不足かも) f:id:contemporarycuz:20190717001056p:plain

[]が修飾される名詞、<>が修飾

正しい読み方
Once the function is created, add [a new file] <in the same folder> <named index.test.js>
in ~ と named ~がそれぞれ a new fileを修飾している

ダメな読み方(混乱する読み方)
Once the function is created, add [a new file] <in [the same folder] <named index.test.js>>
in から後ろが a new fileを修飾している。 named ~ が the same folder を修飾している。

よって、悩んだときはドキュメントを隅々まで読む。英語を読む(読んでも経験上無意味なことが多いけど)ことが重要ですね。