Skip to content

Set up docgen for rules-unit-testing #5419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Oct 14, 2021
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,8 @@ temp
packages/**/docs

# files generated by api-extractor that should not be tracked
tsdoc-metadata.json
tsdoc-metadata.json

# generated html docs
docs-*/
docs/
101 changes: 101 additions & 0 deletions common/api-review/rules-unit-testing.api.md
Original file line number Diff line number Diff line change
@@ -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<any>): Promise<any>;

// @public
export function assertSucceeds<T>(pr: Promise<T>): Promise<T>;

// @public
export type EmulatorConfig = {
rules?: string;
} & (HostAndPort | {});

// @public
export interface HostAndPort {
host: string;
port: number;
}

// @public
export function initializeTestEnvironment(config: TestEnvironmentConfig): Promise<RulesTestEnvironment>;

// @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<void>;
clearDatabase(): Promise<void>;
clearFirestore(): Promise<void>;
clearStorage(): Promise<void>;
readonly emulators: {
database?: HostAndPort;
firestore?: HostAndPort;
storage?: HostAndPort;
};
readonly projectId: string;
unauthenticatedContext(): RulesTestContext;
// (undocumented)
withSecurityRulesDisabled(callback: (context: RulesTestContext) => Promise<void>): Promise<void>;
}

// @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<TResult>(fn: () => TResult | Promise<TResult>): Promise<TResult>;

// @public
export function withFunctionTriggersDisabled<TResult>(hub: {
host: string;
port: number;
}, fn: () => TResult | Promise<TResult>): Promise<TResult>;


// (No @packageDocumentation comment for this package)

```
5 changes: 5 additions & 0 deletions packages/rules-unit-testing/api-extractor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../config/api-extractor.json",
// Point it to your entry point d.ts file.
"mainEntryPointFilePath": "<projectFolder>/dist/index.d.ts"
}
7 changes: 5 additions & 2 deletions packages/rules-unit-testing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion packages/rules-unit-testing/src/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 22 additions & 22 deletions packages/rules-unit-testing/src/public_types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -222,7 +222,7 @@ export interface RulesTestEnvironment {
clearDatabase(): Promise<void>;

/**
* 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<void>;

Expand All @@ -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<void>;
}
Expand All @@ -247,38 +247,38 @@ 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(
settings?: firebase.firestore.Settings
): 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;
}
8 changes: 4 additions & 4 deletions packages/rules-unit-testing/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TResult>(
Expand All @@ -46,8 +46,8 @@ export async function withFunctionTriggersDisabled<TResult>(
* 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<TResult>(
Expand Down Expand Up @@ -121,7 +121,7 @@ export async function withFunctionTriggersDisabled<TResult>(
* 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
Expand Down
50 changes: 46 additions & 4 deletions scripts/docgen/docgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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';
Expand Down Expand Up @@ -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));
Expand All @@ -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<string[]>(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);
}
}