diff --git a/.github/workflows/package-unit-tests.yml b/.github/workflows/package-unit-tests.yml index 3c71535aa13..4b308ca36a9 100644 --- a/.github/workflows/package-unit-tests.yml +++ b/.github/workflows/package-unit-tests.yml @@ -19,27 +19,50 @@ jobs: strategy: fail-fast: false matrix: - workspace: - - realm - - '@realm/bindgen' - - '@realm/network-transport' - - '@realm/babel-plugin' - - '@realm/react' - name: ${{ matrix.workspace }} unit tests + variant: + - {workspace: realm} + - {workspace: '@realm/bindgen'} + - {workspace: '@realm/network-transport'} + - {workspace: '@realm/babel-plugin'} + - {workspace: '@realm/react', use-baas: true} + name: ${{ matrix.variant.workspace }} unit tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: submodules: "recursive" + - uses: actions/setup-node@v3 with: node-version: 18 + # ninja-build is used by default if available and results in faster build times - name: Install ninja run: sudo apt-get install ninja-build + - name: ccache uses: hendrikmuhs/ccache-action@v1 + + - name: Generate server configuration + if: ${{matrix.variant.use-baas}} + id: baas-config + run: + suffix=$(node -p 'Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)'); + subdomain="realm-js-test-server-${{ github.run_id }}-${{ github.run_attempt }}-${suffix}"; + echo "subdomain=${subdomain}" >> $GITHUB_OUTPUT; + echo "url=https://${subdomain}.ngrok.io" >> $GITHUB_OUTPUT; + + - name: Trigger the test server workflow to start the server + if: ${{matrix.variant.use-baas}} + run: gh workflow run test-server.yml -f ngrok_subdomain=${{ steps.baas-config.outputs.subdomain }} -f run_id=${{ github.run_id }} + env: + GH_TOKEN: ${{ github.token }} + + - name: Set baas env + if: ${{matrix.variant.use-baas}} + run: echo "realmBaseUrl=${{ steps.baas-config.outputs.url }}" >> $GITHUB_ENV + # Install the root package to get dev-dependencies # (--ignore-scripts to avoid downloading or building the native module) - - run: npm ci --ignore-scripts - - run: npm test --workspace ${{ matrix.workspace }} + - run: npm ci + - run: npm test --workspace ${{ matrix.variant.workspace }} diff --git a/packages/realm-react/src/__tests__/helpers.ts b/packages/realm-react/src/__tests__/helpers.ts new file mode 100644 index 00000000000..210d0f5e485 --- /dev/null +++ b/packages/realm-react/src/__tests__/helpers.ts @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2020 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +import { AppConfig, AppImporter, Credentials } from "@realm/app-importer"; + +const { realmBaseUrl = "http://localhost:9090" } = process.env; + +export const baseUrl = realmBaseUrl; + +function getCredentials(): Credentials { + const { publicKey, privateKey, username = "unique_user@domain.com", password = "password" } = process.env; + if (typeof publicKey === "string" && typeof privateKey === "string") { + return { + kind: "api-key", + publicKey, + privateKey, + }; + } else { + return { + kind: "username-password", + username, + password, + }; + } +} + +const credentials = getCredentials(); + +const importer = new AppImporter({ + baseUrl: realmBaseUrl, + credentials, +}); + +export async function importApp(config: AppConfig): Promise<{ appId: string }> { + const { appId } = await importer.importApp(config); + return { appId }; +} diff --git a/packages/realm-react/src/__tests__/useAuth.test.tsx b/packages/realm-react/src/__tests__/useAuth.test.tsx index daf195fc95f..b0a20c429db 100644 --- a/packages/realm-react/src/__tests__/useAuth.test.tsx +++ b/packages/realm-react/src/__tests__/useAuth.test.tsx @@ -19,22 +19,13 @@ import React from "react"; import { AppProvider } from "../AppProvider"; import { waitFor, renderHook, act } from "@testing-library/react-native"; -import { AppConfigBuilder, AppImporter, Credentials } from "@realm/app-importer"; -import { ImportedApp } from "@realm/app-importer/src/AppImporter"; +import { AppConfigBuilder } from "@realm/app-importer"; import { useAuth } from "../useAuth"; +import { baseUrl, importApp } from "./helpers"; -const credentials: Credentials = { - kind: "username-password", - username: "unique_user@domain.com", - password: "password", -}; - -const baseUrl = "http://localhost:9090"; -const appImporter = new AppImporter({ baseUrl, credentials }); - -function renderAuth(importedApp: ImportedApp) { +function renderAuth(appId: string, baseUrl: string) { const wrapper = ({ children }: { children: React.ReactNode }) => ( - + {children} ); @@ -45,13 +36,13 @@ function renderAuth(importedApp: ImportedApp) { // The tests for the authentication methods themselves should be written elsewhere describe("useAuth", () => { describe("all methods are callable and report a state", () => { - let importedApp: ImportedApp; + let appId: string; beforeAll(async () => { const config = new AppConfigBuilder("test-app"); - importedApp = await appImporter.importApp(config.config); + ({ appId } = await importApp(config.config)); }); it("logIn", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logIn({ email: "test@test.com", password: "password" }); await waitFor(() => { @@ -63,7 +54,7 @@ describe("useAuth", () => { }); }); it("logInWithAnonymous", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithAnonymous(); await waitFor(() => { @@ -75,7 +66,7 @@ describe("useAuth", () => { }); }); it("logInWithApiKey", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithApiKey("12345"); await waitFor(() => { @@ -87,7 +78,7 @@ describe("useAuth", () => { }); }); it("logInWithEmailPassword", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithEmailPassword({ email: "test@test.com", password: "password" }); await waitFor(() => { @@ -99,7 +90,7 @@ describe("useAuth", () => { }); }); it("logInWithJWT", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithJWT("token"); await waitFor(() => { @@ -111,7 +102,7 @@ describe("useAuth", () => { }); }); it("logInWithGoogle", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithGoogle({ idToken: "1234" }); await waitFor(() => { @@ -123,7 +114,7 @@ describe("useAuth", () => { }); }); it("logInWithApple", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithApple("token"); await waitFor(() => { @@ -135,7 +126,7 @@ describe("useAuth", () => { }); }); it("logInWithFacebook", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithFacebook("token"); await waitFor(() => { @@ -147,7 +138,7 @@ describe("useAuth", () => { }); }); it("logInWithFunction", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logInWithFunction({ foo: "bar" }); await waitFor(() => { @@ -159,7 +150,7 @@ describe("useAuth", () => { }); }); it("logOut", async () => { - const { result } = renderAuth(importedApp); + const { result } = renderAuth(appId, baseUrl); await act(async () => { result.current.logOut(); await waitFor(() => { diff --git a/packages/realm-react/src/__tests__/useEmailPasswordAuth.test.tsx b/packages/realm-react/src/__tests__/useEmailPasswordAuth.test.tsx index d72a7b6688c..ad8f66b361a 100644 --- a/packages/realm-react/src/__tests__/useEmailPasswordAuth.test.tsx +++ b/packages/realm-react/src/__tests__/useEmailPasswordAuth.test.tsx @@ -19,24 +19,15 @@ import React from "react"; import { AppProvider } from "../AppProvider"; import { waitFor, renderHook, act } from "@testing-library/react-native"; -import { AppConfigBuilder, AppImporter, Credentials } from "@realm/app-importer"; +import { AppConfigBuilder } from "@realm/app-importer"; import { App } from "realm"; -import { ImportedApp } from "@realm/app-importer/src/AppImporter"; import { useEmailPasswordAuth } from "../useEmailPasswordAuth"; import { OperationState } from "../types"; +import { baseUrl, importApp } from "./helpers"; -const credentials: Credentials = { - kind: "username-password", - username: "unique_user@domain.com", - password: "password", -}; - -const baseUrl = "http://localhost:9090"; -const appImporter = new AppImporter({ baseUrl, credentials }); - -function renderEmailPasswordAuth(importedApp: ImportedApp) { +function renderEmailPasswordAuth(appId: string, baseUrl: string) { const wrapper = ({ children }: { children: React.ReactNode }) => ( - + {children} ); @@ -45,7 +36,7 @@ function renderEmailPasswordAuth(importedApp: ImportedApp) { describe("useEmailPassword", () => { describe("with auto confirm", () => { - let importedApp: ImportedApp; + let appId: string; beforeAll(async () => { const config = new AppConfigBuilder("test-app"); config.authProvider({ @@ -57,10 +48,10 @@ describe("useEmailPassword", () => { }, disabled: false, }); - importedApp = await appImporter.importApp(config.config); + ({ appId } = await importApp(config.config)); }); it("can register and login with email/password, just by calling register", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.register({ email: "test@test.com", password: "password" }); await waitFor(() => { @@ -72,7 +63,7 @@ describe("useEmailPassword", () => { }); // Get an instance of the realm app and make sure the current user has been set - const realmApp = new App({ id: importedApp.appId, baseUrl }); + const realmApp = new App({ id: appId, baseUrl }); expect(realmApp.currentUser).not.toBeNull(); await act(async () => { await result.current.logOut(); @@ -80,7 +71,7 @@ describe("useEmailPassword", () => { expect(realmApp.currentUser).toBeNull(); }); it("can register and login with email/password, just by calling register and login", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.register({ email: "test2@test.com", password: "password", loginAfterRegister: false }); await waitFor(() => { @@ -102,7 +93,7 @@ describe("useEmailPassword", () => { }); // Get an instance of the realm app and make sure the current user has been set - const realmApp = new App({ id: importedApp.appId, baseUrl }); + const realmApp = new App({ id: appId, baseUrl }); const user = realmApp.currentUser; expect(user).not.toBeNull(); @@ -112,7 +103,7 @@ describe("useEmailPassword", () => { expect(realmApp.currentUser).toBeNull(); }); it("sets an error state when user is already registered", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.register({ email: "test2@test.com", password: "password", loginAfterRegister: false }), @@ -127,7 +118,7 @@ describe("useEmailPassword", () => { }); }); describe("all methods are callable and report a state", () => { - let importedApp: ImportedApp; + let appId: string; beforeAll(async () => { const config = new AppConfigBuilder("test-app-2"); config @@ -182,10 +173,10 @@ describe("useEmailPassword", () => { }; `, }); - importedApp = await appImporter.importApp(config.config); + ({ appId } = await importApp(config.config)); }); it("logIn", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.logIn({ email: "test@test.com", password: "password" }); await waitFor(() => { @@ -197,7 +188,7 @@ describe("useEmailPassword", () => { }); }); it("register", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.register({ email: "test@test.com", password: "password" }); await waitFor(() => { @@ -209,7 +200,7 @@ describe("useEmailPassword", () => { }); }); it("confirm", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.confirm({ token: "1234", tokenId: "4321" }); await waitFor(() => { @@ -221,7 +212,7 @@ describe("useEmailPassword", () => { }); }); it("resendConfirmationEmail", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.resendConfirmationEmail({ email: "test@test.com" }); await waitFor(() => { @@ -233,7 +224,7 @@ describe("useEmailPassword", () => { }); }); it("retryCustomConfirmation", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.retryCustomConfirmation({ email: "test@test.com" }); await waitFor(() => { @@ -245,7 +236,7 @@ describe("useEmailPassword", () => { }); }); it("sendResetPasswordEmail", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.sendResetPasswordEmail({ email: "test@test.com" }); await waitFor(() => { @@ -257,7 +248,7 @@ describe("useEmailPassword", () => { }); }); it("resetPassword", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.resetPassword({ token: "1234", tokenId: "4321", password: "newpassword" }); await waitFor(() => { @@ -269,7 +260,7 @@ describe("useEmailPassword", () => { }); }); it("callResetPasswordFunction", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.callResetPasswordFunction({ email: "test@test.com", password: "password" }, { foo: "bar" }); await waitFor(() => { @@ -281,7 +272,7 @@ describe("useEmailPassword", () => { }); }); it("logOut", async () => { - const { result } = renderEmailPasswordAuth(importedApp); + const { result } = renderEmailPasswordAuth(appId, baseUrl); await act(async () => { result.current.logOut(); await waitFor(() => {