Skip to content

Commit f333c60

Browse files
authored
Added storage tests (#11)
Add tests for the Firebase Storage SDK.
1 parent 25b011d commit f333c60

File tree

8 files changed

+336
-7
lines changed

8 files changed

+336
-7
lines changed

README.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Firebase SDK Next JS Tests
22

3-
This repository contains automatic nightly tests that exercise the Firebase JS SDK within a Next.JS app environment.
3+
This repository contains automatic nightly tests that exercise the Firebase JS SDK within a Next.JS app environment via the Playwright test framework.
44

55

66
## Getting Started
77

8+
### Tools
89
This project has be built and tested with `yarn` version `1.22.11`.
910

1011
First, install dependencies:
@@ -13,27 +14,34 @@ First, install dependencies:
1314
yarn
1415
```
1516

16-
To configure your Firebase project data:
17+
### Firebase Project configuration
18+
To configure your Firebase project data, add your project configuration to `./lib/firebase.ts`.
1719

18-
**TBD**
1920

20-
As of now, add your firbase config data to `./lib/firebase.ts`.
21-
22-
To build the test apps:
21+
### Building and executing the tests:
2322

2423
```bash
2524
yarn build
2625
```
2726

28-
To execute the tests, run:
27+
To execute all of the tests, run:
2928

3029
```bash
3130
yarn test
3231
```
3332

33+
To exeucte a single test, define the playwright test spec as a command line parameter. For instance,
34+
to execute only the Auth tests:
35+
36+
```bash
37+
yarn test tests/auth.spec.ts
38+
```
39+
3440
To run the tests on an app server for manual testing:
3541

3642
```bash
3743
yarn dev
3844
```
3945

46+
47+

app/page.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ export default async function Page() {
7878
<li><Link href="/tests/performance/web_client">Performance Web SDK client-side tests</Link></li>
7979
<li><Link href="/tests/performance/web_ssr">Performance Web SDK server-side tests</Link></li>
8080
</ul>
81+
<p />
82+
<li>Storage</li>
83+
<ul>
84+
<li><Link href="/tests/storage/web_client">Storage Web SDK client-side tests</Link></li>
85+
<li><Link href="/tests/storage/web_ssr">Storage Web SDK server-side tests</Link></li>
86+
</ul>
8187
</ul>
8288
</>
8389
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
'use client'
18+
19+
import { useState, useEffect } from 'react'
20+
import { testStorage, initializeTestResults } from '../lib/test';
21+
import ResultsDisplay from './results_display';
22+
23+
export default function CsrTestRunner() {
24+
const [testStatus, setTestStatus] = useState<string>("running...");
25+
const [testResults, setTestResults] = useState(initializeTestResults());
26+
useEffect(() => {
27+
const asyncTest = async () => {
28+
setTestResults(await testStorage());
29+
setTestStatus("Complete!");
30+
}
31+
asyncTest().catch((e) => {
32+
console.error("Error encountered during testing: ", e);
33+
setTestStatus("Errored!");
34+
});
35+
}, []);
36+
37+
return (
38+
<ResultsDisplay statusString={testStatus} testResults={testResults} />
39+
);
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import Link from 'next/link';
18+
export default function ResultsDisplay({ statusString, testResults }) {
19+
return (
20+
<>
21+
<h2 title="testStatus">{statusString}</h2>
22+
<h4 title="initializeAppResult">initializeAppResult: {testResults.initializeAppResult}</h4>
23+
<h4 title="initializeAuthResult">initializeAuthResult: {testResults.initializeAuthResult}</h4>
24+
<h4 title="authUserSignedInResult">authUserSignedInResult: {testResults.authUserSignedInResult}</h4>
25+
<h4 title="initializeStorageResult">initializeStorageResult: {testResults.initializeStorageResult}</h4>
26+
<h4 title="createStorageRefResult">createStorageRefResult: {testResults.createStorageRefResult}</h4>
27+
<h4 title="uploadStringResult">uploadStringResult: {testResults.uploadStringResult}</h4>
28+
<h4 title="getDownloadUrlResult">getDownloadUrlResult: {testResults.getDownloadUrlResult}</h4>
29+
<h4 title="fetchCompletedResult">fetchCompletedResult: {testResults.fetchCompletedResult}</h4>
30+
<h4 title="getResponseTextResult">getResponseTextResult: {testResults.getResponseTextResult}</h4>
31+
<h4 title="getDataMatchesExepctedResult">getDataMatchesExepctedResult: {testResults.getDataMatchesExepctedResult}</h4>
32+
<h4 title="deleteReferenceResult">deleteReferenceResult: {testResults.deleteReferenceResult}</h4>
33+
<h4 title="deleteUserResult">deleteUserResult: {testResults.deleteUserResult}</h4>
34+
<h4 title="authSignedOutResult">authSignedOutResult: {testResults.authSignedOutResult}</h4>
35+
<h4 title="deleteAppResult">deleteAppResult: {testResults.deleteAppResult}</h4>
36+
<p />
37+
<Link href="/">Back to test index</Link>
38+
</>
39+
);
40+
}

app/tests/storage/lib/test.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { deleteUser, getAuth, signInAnonymously } from 'firebase/auth';
19+
import { deleteApp, initializeApp } from 'firebase/app';
20+
import { firebaseConfig } from 'lib/firebase';
21+
import { deleteObject, getDownloadURL, getStorage, ref, uploadString } from 'firebase/storage';
22+
import { OK, FAILED, waitForUserSignIn } from 'lib/util';
23+
24+
export type TestResults = {
25+
initializeAppResult: string,
26+
initializeAuthResult: string,
27+
authUserSignedInResult: string,
28+
initializeStorageResult: string,
29+
createStorageRefResult: string,
30+
uploadStringResult: string,
31+
getDownloadUrlResult: string,
32+
fetchCompletedResult: string,
33+
getResponseTextResult: string,
34+
getDataMatchesExepctedResult: string,
35+
deleteReferenceResult: string,
36+
deleteUserResult: string,
37+
authSignedOutResult,
38+
deleteAppResult: string
39+
};
40+
41+
export function initializeTestResults(): TestResults {
42+
return {
43+
initializeAppResult: FAILED,
44+
initializeAuthResult: FAILED,
45+
authUserSignedInResult: FAILED,
46+
initializeStorageResult: FAILED,
47+
createStorageRefResult: FAILED,
48+
uploadStringResult: FAILED,
49+
getDownloadUrlResult: FAILED,
50+
fetchCompletedResult: FAILED,
51+
getResponseTextResult: FAILED,
52+
getDataMatchesExepctedResult: FAILED,
53+
deleteReferenceResult: FAILED,
54+
deleteUserResult: FAILED,
55+
authSignedOutResult: FAILED,
56+
deleteAppResult: FAILED
57+
};
58+
}
59+
60+
export async function testStorage(isServer: boolean = false): Promise<TestResults> {
61+
const result: TestResults = initializeTestResults();
62+
if (isServer) {
63+
console.log("server app");
64+
}
65+
66+
try {
67+
const firebaseApp = initializeApp(firebaseConfig);
68+
if (firebaseApp === null) {
69+
return result;
70+
}
71+
result.initializeAppResult = OK;
72+
73+
const auth = getAuth(firebaseApp);
74+
await auth.authStateReady();
75+
result.initializeAuthResult = OK;
76+
77+
await signInAnonymously(auth);
78+
await waitForUserSignIn(auth);
79+
if (auth.currentUser) {
80+
result.authUserSignedInResult = OK;
81+
82+
const storage = getStorage(firebaseApp);
83+
if (storage) {
84+
result.initializeStorageResult = OK;
85+
const reference = ref(storage, '/next-js-test.txt');
86+
if (reference) {
87+
const storageText = 'next-js-test-string';
88+
result.createStorageRefResult = OK;
89+
await uploadString(reference, storageText);
90+
result.uploadStringResult = OK;
91+
const url = await getDownloadURL(reference);
92+
if (url) {
93+
result.getDownloadUrlResult = OK;
94+
const response = await fetch(url);
95+
result.fetchCompletedResult = OK;
96+
const data = await response.text();
97+
result.getResponseTextResult = OK;
98+
if (data === storageText) {
99+
result.getDataMatchesExepctedResult = OK;
100+
}
101+
}
102+
await deleteObject(reference);
103+
result.deleteReferenceResult = OK;
104+
}
105+
}
106+
await deleteUser(auth.currentUser);
107+
result.deleteUserResult = OK;
108+
109+
await auth.signOut();
110+
result.authSignedOutResult = OK;
111+
}
112+
deleteApp(firebaseApp);
113+
result.deleteAppResult = OK;
114+
} catch (e) {
115+
console.error("Caught error: ", e);
116+
}
117+
return result;
118+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import type { Metadata } from 'next'
18+
import CSRTestRunner from '../components/csr_test_runner';
19+
20+
export const metadata: Metadata = {
21+
title: 'Storage Web SDK CSR test'
22+
}
23+
24+
export default function Page() {
25+
return (
26+
<>
27+
<h1>Storage CSR Test results:</h1>
28+
<CSRTestRunner />
29+
</>
30+
);
31+
}

app/tests/storage/web_ssr/page.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import type { Metadata } from 'next'
18+
import { testStorage, TestResults } from '../lib/test';
19+
import ResultsDisplay from '../components/results_display';
20+
21+
// Suppress static site generation.
22+
export const dynamic = "force-dynamic";
23+
24+
export const metadata: Metadata = {
25+
title: 'Storage Web SDK SSR test'
26+
}
27+
28+
export default async function Page() {
29+
const testResults: TestResults = await testStorage(/*isServer=*/true);
30+
return (
31+
<>
32+
<h1>Storage SSR Test results:</h1>
33+
<ResultsDisplay statusString='Tests Complete!' testResults={testResults} />
34+
</>
35+
);
36+
}

tests/storage.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { test, expect } from '@playwright/test';
18+
19+
test.describe.configure({ mode: 'serial' });
20+
21+
async function commonExpectations(page) {
22+
await expect(page.getByTitle('initializeAppResult')).not.toContainText("FAILED");
23+
await expect(page.getByTitle('initializeAuthResult')).not.toContainText("FAILED");
24+
await expect(page.getByTitle('authUserSignedInResult')).not.toContainText("FAILED");
25+
await expect(page.getByTitle('initializeStorageResult')).not.toContainText("FAILED");
26+
await expect(page.getByTitle('createStorageRefResult')).not.toContainText("FAILED");
27+
await expect(page.getByTitle('uploadStringResult')).not.toContainText("FAILED");
28+
await expect(page.getByTitle('getDownloadUrlResult')).not.toContainText("FAILED");
29+
await expect(page.getByTitle('fetchCompletedResult')).not.toContainText("FAILED");
30+
await expect(page.getByTitle('getResponseTextResult')).not.toContainText("FAILED");
31+
await expect(page.getByTitle('getDataMatchesExepctedResult')).not.toContainText("FAILED");
32+
await expect(page.getByTitle('deleteReferenceResult')).not.toContainText("FAILED");
33+
await expect(page.getByTitle('deleteUserResult')).not.toContainText("FAILED");
34+
await expect(page.getByTitle('authSignedOutResult')).not.toContainText("FAILED");
35+
await expect(page.getByTitle('deleteAppResult')).not.toContainText("FAILED");
36+
}
37+
38+
test('storage operations should pass - client', async ({ page, baseURL }) => {
39+
await page.goto(`${baseURL}/tests/storage/web_client`);
40+
await expect(page.getByTitle('testStatus')).toContainText('Complete', { timeout: 10000 });
41+
await expect(page.locator('h1')).toContainText('Storage CSR Test');
42+
await commonExpectations(page);
43+
});
44+
45+
test('storage operations should pass - server', async ({ page, baseURL }) => {
46+
await page.goto(`${baseURL}/tests/storage/web_ssr`);
47+
await expect(page.getByTitle('testStatus')).toContainText('Complete', { timeout: 10000 });
48+
await expect(page.locator('h1')).toContainText('Storage SSR Test');
49+
await commonExpectations(page);
50+
});

0 commit comments

Comments
 (0)