diff --git a/.gitignore b/.gitignore index e484478497a..4efb07121e5 100644 --- a/.gitignore +++ b/.gitignore @@ -89,4 +89,8 @@ temp packages/**/docs # files generated by api-extractor that should not be tracked -tsdoc-metadata.json \ No newline at end of file +tsdoc-metadata.json + +# generated html docs +docs-*/ +docs/ \ No newline at end of file diff --git a/common/api-review/rules-unit-testing.api.md b/common/api-review/rules-unit-testing.api.md new file mode 100644 index 00000000000..b4aaf9bc379 --- /dev/null +++ b/common/api-review/rules-unit-testing.api.md @@ -0,0 +1,101 @@ +## API Report File for "@firebase/rules-unit-testing" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { default as firebase_2 } from 'firebase/compat/app'; +import { FirebaseSignInProvider } from '@firebase/util'; + +// @public +export function assertFails(pr: Promise): Promise; + +// @public +export function assertSucceeds(pr: Promise): Promise; + +// @public +export type EmulatorConfig = { + rules?: string; +} & (HostAndPort | {}); + +// @public +export interface HostAndPort { + host: string; + port: number; +} + +// @public +export function initializeTestEnvironment(config: TestEnvironmentConfig): Promise; + +// @public +export interface RulesTestContext { + database(databaseURL?: string): firebase_2.database.Database; + firestore(settings?: firebase_2.firestore.Settings): firebase_2.firestore.Firestore; + storage(bucketUrl?: string): firebase_2.storage.Storage; +} + +// @public +export interface RulesTestEnvironment { + authenticatedContext(user_id: string, tokenOptions?: TokenOptions): RulesTestContext; + cleanup(): Promise; + clearDatabase(): Promise; + clearFirestore(): Promise; + clearStorage(): Promise; + readonly emulators: { + database?: HostAndPort; + firestore?: HostAndPort; + storage?: HostAndPort; + }; + readonly projectId: string; + unauthenticatedContext(): RulesTestContext; + // (undocumented) + withSecurityRulesDisabled(callback: (context: RulesTestContext) => Promise): Promise; +} + +// @public +export interface TestEnvironmentConfig { + database?: EmulatorConfig; + firestore?: EmulatorConfig; + hub?: HostAndPort; + projectId?: string; + storage?: EmulatorConfig; +} + +// @public +export type TokenOptions = { + iat?: number; + exp?: number; + auth_time?: number; + provider_id?: 'anonymous'; + email?: string; + email_verified?: boolean; + phone_number?: string; + name?: string; + picture?: string; + firebase?: { + sign_in_provider: FirebaseSignInProvider; + identities?: { + [provider in FirebaseSignInProvider]?: string[]; + }; + }; + aud?: string; + iss?: string; + [claim: string]: unknown; + uid?: never; + sub?: never; + user_id?: never; +}; + +// @public +export function withFunctionTriggersDisabled(fn: () => TResult | Promise): Promise; + +// @public +export function withFunctionTriggersDisabled(hub: { + host: string; + port: number; +}, fn: () => TResult | Promise): Promise; + + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/rules-unit-testing/api-extractor.json b/packages/rules-unit-testing/api-extractor.json new file mode 100644 index 00000000000..620d10a071c --- /dev/null +++ b/packages/rules-unit-testing/api-extractor.json @@ -0,0 +1,5 @@ +{ + "extends": "../../config/api-extractor.json", + // Point it to your entry point d.ts file. + "mainEntryPointFilePath": "/dist/index.d.ts" +} \ No newline at end of file diff --git a/packages/rules-unit-testing/package.json b/packages/rules-unit-testing/package.json index 08dfb02772b..867e3357566 100644 --- a/packages/rules-unit-testing/package.json +++ b/packages/rules-unit-testing/package.json @@ -11,12 +11,15 @@ "dist" ], "scripts": { - "build": "rollup -c", + "build": "rollup -c && yarn api-report", "build:deps": "lerna run --scope @firebase/rules-unit-testing --include-dependencies build", "dev": "rollup -c -w", "test:nyc": "TS_NODE_CACHE=NO TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'test/{,!(browser)/**/}*.test.ts' --config ./mocharc.node.js", "test": "(cd functions && yarn) && firebase --project=demo-foo --debug emulators:exec 'yarn test:nyc'", - "test:ci": "node ../../scripts/run_tests_in_ci.js -s test" + "test:ci": "node ../../scripts/run_tests_in_ci.js -s test", + "api-report": "api-extractor run --local --verbose", + "doc": "api-documenter markdown --input temp --output docs", + "build:doc": "yarn build && yarn doc" }, "license": "Apache-2.0", "devDependencies": { diff --git a/packages/rules-unit-testing/src/initialize.ts b/packages/rules-unit-testing/src/initialize.ts index c86c67a9ce0..d0442e24524 100644 --- a/packages/rules-unit-testing/src/initialize.ts +++ b/packages/rules-unit-testing/src/initialize.ts @@ -38,7 +38,7 @@ import { * variables or through the Firebase Emulator hub if hosts and ports are unspecified. It is strongly * recommended to specify security rules for emulators used for testing. See minimal example below. * - * @param config the configuration for emulators. most fields are optional if they can be discovered + * @param config - the configuration for emulators. Most fields are optional if they can be discovered * @returns a promise that resolves with an environment ready for testing, or rejects on error. * @public * @example diff --git a/packages/rules-unit-testing/src/public_types/index.ts b/packages/rules-unit-testing/src/public_types/index.ts index 88d54fd8e25..2e6a2812806 100644 --- a/packages/rules-unit-testing/src/public_types/index.ts +++ b/packages/rules-unit-testing/src/public_types/index.ts @@ -131,13 +131,13 @@ export interface TestEnvironmentConfig { export interface HostAndPort { /** * The host of the emulator. Can be omitted if discovered automatically through the hub or - * specified via environment variables. See {@code TestEnvironmentConfig} for details. + * specified via environment variables. See `TestEnvironmentConfig` for details. */ host: string; /** * The port of the emulator. Can be omitted if discovered automatically through the hub or - * specified via environment variables. See {@code TestEnvironmentConfig} for details. + * specified via environment variables. See `TestEnvironmentConfig` for details. */ port: number; } @@ -170,12 +170,12 @@ export interface RulesTestEnvironment { }; /** - * Create a {@code RulesTestContext} which behaves like an authenticated Firebase Auth user. + * Create a `RulesTestContext` which behaves like an authenticated Firebase Auth user. * * Requests created via the returned context will have a mock Firebase Auth token attached. * - * @param user_id the User ID of the user. Specifies the value of "user_id" and "sub" on the token - * @param tokenOptions custom claims or overrides for Firebase Auth token payloads + * @param user_id - the User ID of the user. Specifies the value of "user_id" and "sub" on the token + * @param tokenOptions - custom claims or overrides for Firebase Auth token payloads * * @example * ```javascript @@ -189,7 +189,7 @@ export interface RulesTestEnvironment { ): RulesTestContext; /** - * Create a {@code RulesTestContext} which behaves like client that is NOT logged in via Firebase + * Create a `RulesTestContext` which behaves like client that is NOT logged in via Firebase * Auth. * * Requests created via the returned context will not have Firebase Auth tokens attached. @@ -222,7 +222,7 @@ export interface RulesTestEnvironment { clearDatabase(): Promise; /** - * Clear data in the Firestore that belongs to the {@code projectId} in the Firestore emulator. + * Clear data in the Firestore that belongs to the `projectId` in the Firestore emulator. */ clearFirestore(): Promise; @@ -236,7 +236,7 @@ export interface RulesTestEnvironment { * created in test environment and clean up the underlying resources, allowing a clean exit. * * This method does not change the state in emulators in any way. To reset data between tests, - * see {@code clearDatabase()}, {@code clearFirestore()} and {@code clearStorage()}. + * see `clearDatabase()`, `clearFirestore()` and `clearStorage()`. */ cleanup(): Promise; } @@ -247,12 +247,12 @@ export interface RulesTestEnvironment { */ export interface RulesTestContext { /** - * Get a Firestore instance for this test context. The returned Firebase JS Client SDK instance + * Get a {@link @firebase/firestore#Firestore} instance for this test context. The returned Firebase JS Client SDK instance * can be used with the client SDK APIs (v9 modular or v9 compat). * - * See: https://firebase.google.com/docs/reference/js/v9/firestore_ - * @param settings a settings object to configure the {@code Firestore} instance - * @returns a Firestore instance configured to connect to the emulator + * See: {@link @firebase/firestore#Firestore} + * @param settings - a settings object to configure the {@link @firebase/firestore#Firestore} instance + * @returns a `Firestore` instance configured to connect to the emulator * @public */ firestore( @@ -260,25 +260,25 @@ export interface RulesTestContext { ): firebase.firestore.Firestore; /** - * Get a Firestore instance for this test context. The returned Firebase JS Client SDK instance + * Get a {@link @firebase/database#Database} instance for this test context. The returned Firebase JS Client SDK instance * can be used with the client SDK APIs (v9 modular or v9 compat). * - * See: https://firebase.google.com/docs/reference/js/v9/firestore_ - * @param databaseURL the URL of the Realtime Database instance. If specified, returns an instance + * See: {@link @firebase/database#Database} + * @param databaseURL - the URL of the Realtime Database instance. If specified, returns an instance * for an emulated version of the namespace with parameters extracted from URL - * @returns a Database instance configured to connect to the emulator. It never connects to - * production even if a production databaseURL is specified + * @returns a `Database` instance configured to connect to the emulator. It never connects to + * production even if a production `databaseURL` is specified */ database(databaseURL?: string): firebase.database.Database; /** - * Get a Storage instance for this test context. The returned Firebase JS Client SDK instance + * Get a {@link @firebase/storage#FirebaseStorage} instance for this test context. The returned Firebase JS Client SDK instance * can be used with the client SDK APIs (v9 modular or v9 compat). * - * See: https://firebase.google.com/docs/reference/js/v9/firestore_ - * @param settings the gs:// url to the Firebase Storage Bucket for testing. If specified, - * returns a Storage instance for an emulated version of the bucket name - * @returns a Storage instance configured to connect to the emulator + * See: {@link @firebase/storage#FirebaseStorage} + * @param settings - the gs:// url to the Firebase Storage Bucket for testing. If specified, + * returns a `Storage` instance for an emulated version of the bucket name + * @returns a `Storage` instance configured to connect to the emulator */ storage(bucketUrl?: string): firebase.storage.Storage; } diff --git a/packages/rules-unit-testing/src/util.ts b/packages/rules-unit-testing/src/util.ts index 437cefcf6b5..e6f7cc87b9c 100644 --- a/packages/rules-unit-testing/src/util.ts +++ b/packages/rules-unit-testing/src/util.ts @@ -31,7 +31,7 @@ import fetch from 'node-fetch'; * This method only works with Firebase CLI version 8.13.0 or higher. This overload works only if * the Emulator hub host:port is specified by the environment variable FIREBASE_EMULATOR_HUB. * - * @param fn an function which may be sync or async (returns a promise) + * @param fn - a function which may be sync or async (returns a promise) * @public */ export async function withFunctionTriggersDisabled( @@ -46,8 +46,8 @@ export async function withFunctionTriggersDisabled( * This method only works with Firebase CLI version 8.13.0 or higher. The Emulator hub must be * running, which host and port are specified in this overload. * - * @param fn an function which may be sync or async (returns a promise) - * @param hub the host and port of the Emulator Hub (ex: `{host: 'localhost', port: 4400}`) + * @param fn - a function which may be sync or async (returns a promise) + * @param hub - the host and port of the Emulator Hub (ex: `{host: 'localhost', port: 4400}`) * @public */ export async function withFunctionTriggersDisabled( @@ -121,7 +121,7 @@ export async function withFunctionTriggersDisabled( * Useful to assert a certain request to be denied by Security Rules. See example below. * This function recognizes permission-denied errors from Database, Firestore, and Storage JS SDKs. * - * @param pr the promise to be asserted + * @param pr - the promise to be asserted * @returns a Promise that is fulfilled if pr is rejected with "permission denied". If pr is * rejected with any other error or resolved, the returned promise rejects. * @public diff --git a/scripts/docgen/docgen.ts b/scripts/docgen/docgen.ts index c655c594ae9..d87815ea83c 100644 --- a/scripts/docgen/docgen.ts +++ b/scripts/docgen/docgen.ts @@ -22,6 +22,8 @@ import fs from 'fs'; import glob from 'glob'; import * as yargs from 'yargs'; +const tmpDir = `${projectRoot}/temp`; + yargs .command('$0', 'generate standard reference docs', {}, _argv => generateDocs(/* forDevsite */ false) @@ -32,7 +34,6 @@ yargs .demandCommand() .help().argv; -const tmpDir = `${projectRoot}/temp`; // create *.api.json files async function generateDocs(forDevsite: boolean = false) { const outputFolder = forDevsite ? 'docs-devsite' : 'docs'; @@ -72,9 +73,7 @@ async function generateDocs(forDevsite: boolean = false) { // TODO: Throw error if path doesn't exist once all packages add markdown support. const apiJsonDirectories = ( - await mapWorkspaceToPackages([ - `${projectRoot}/packages/*` - ]) + await mapWorkspaceToPackages([`${projectRoot}/packages/*`]) ) .map(path => `${path}/temp`) .filter(path => fs.existsSync(path)); @@ -101,4 +100,47 @@ async function generateDocs(forDevsite: boolean = false) { [command, 'markdown', '--input', 'temp', '--output', outputFolder], { stdio: 'inherit' } ); + + moveRulesUnitTestingDocs(outputFolder, command); +} + +// Create a docs-rut folder and move rules-unit-testing docs into it. +async function moveRulesUnitTestingDocs( + mainDocsFolder: string, + command: string +) { + const rulesOutputFolder = `${projectRoot}/docs-rut`; + + if (!fs.existsSync(rulesOutputFolder)) { + fs.mkdirSync(rulesOutputFolder); + } + + const rulesDocPaths = await new Promise(resolve => + glob(`${mainDocsFolder}/rules-unit-testing.*`, (err, paths) => { + if (err) throw err; + resolve(paths); + }) + ); + // Move rules-unit-testing docs into the new folder. + // These paths also need to be adjusted to point to a sibling directory. + for (const sourcePath of rulesDocPaths) { + let destinationPath = sourcePath.replace(mainDocsFolder, rulesOutputFolder); + + const originalText = fs.readFileSync(sourcePath, 'utf-8'); + const jsReferencePath = '/docs/reference/js'; + let alteredPathText = originalText.replace( + /\.\/database/g, + `${jsReferencePath}/database` + ); + alteredPathText = alteredPathText.replace( + /\.\/storage/g, + `${jsReferencePath}/storage` + ); + alteredPathText = alteredPathText.replace( + /\.\/firestore/g, + `${jsReferencePath}/firestore` + ); + fs.writeFileSync(destinationPath, alteredPathText); + fs.unlinkSync(sourcePath); + } }