Repository: GoogleChromeLabs/ps-analysis-tool
Pull request #728 (2024-06-26 05:26:43)

CLI: Fix missing frame key in the page frames Hashmap
## Description

This PR fixes an intermittently occurring error bug when analyzing sites.
![Screenshot 2024-06-25 at 7 53 54 AM](https://github.com/GoogleChromeLabs/ps-analysis-tool/assets/53055971/941bdb85-e14c-4183-a159-2c66844ed588)


## Checklist

<!-- Check these after PR creation -->

- [x] I have thoroughly tested this code to the best of my abilities.
- [x] I have reviewed the code myself before requesting a review.
- ~[ ] This code is covered by unit tests to verify that it works as intended.~
- [x] The QA of this PR is done by a member of the QA team (to be checked by QA).

<!--
Example:

Fixes #123
Partially addresses #22
See #834
-->


Patch:
diff --git a/package-lock.json b/package-lock.json
index 571e33b5..33bbfd10 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21766,6 +21766,7 @@
 			"license": "Apache-2.0",
 			"dependencies": {
 				"@google-psat/analysis-utils": "*",
+				"@google-psat/cli-dashboard": "*",
 				"@google-psat/common": "*",
 				"@google-psat/i18n": "*",
 				"@google-psat/library-detection": "*",
@@ -21787,9 +21788,6 @@
 				"tsc-watch": "^6.0.4",
 				"typescript": "^5.0.4",
 				"webpack": "^5.86.0"
-			},
-			"peerDependencies": {
-				"@google-psat/cli-dashboard": "*"
 			}
 		},
 		"packages/cli-dashboard": {
diff --git a/package.json b/package.json
index 1957d909..a489e248 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
 		"build-cli": "npm run cli-dashboard:build && npm run cli:build",
 		"publish:all:local": "npm run build:all && npm run publish:local --workspaces",
 		"unpublish:all:local": "npm run unpublish:local --workspaces",
-		"publish:all:remote": "npm run build:all && npm run publish:local --workspaces",
+		"publish:all:remote": "npm run build:all && npm run publish:remote --workspaces",
 		"unpublish:all:remote": "npm run unpublish:remote --workspaces",
 		"i18n:prebuild": "npm run build:remove -w @google-psat/i18n",
 		"i18n:build": "npm run i18n:prebuild && npm run build -w @google-psat/i18n",
diff --git a/packages/analysis-utils/src/browserManagement/index.ts b/packages/analysis-utils/src/browserManagement/index.ts
index c466748e..42325659 100644
--- a/packages/analysis-utils/src/browserManagement/index.ts
+++ b/packages/analysis-utils/src/browserManagement/index.ts
@@ -666,7 +666,13 @@ export class BrowserManagement {
     const mainFrameUrlIdMap = await this.getMainframeIds();
 
     Object.entries(mainFrameUrlIdMap).forEach(([_url, id]) => {
-      this.pageFrames[_url][id] = '0';
+      if (!this.pageFrames[_url]) {
+        this.pageFrames[_url] = {
+          [id]: '0',
+        };
+      } else {
+        this.pageFrames[_url][id] = '0';
+      }
     });
 
     const result = await Promise.all(
diff --git a/packages/cli/README.md b/packages/cli/README.md
index 8dc66e49..987790c3 100644
--- a/packages/cli/README.md
+++ b/packages/cli/README.md
@@ -1,11 +1,17 @@
-> The Privacy Sandbox Analysis Tool CLI aims to empower developers to prepare their sites/applications for the upcoming deprecation of Third-party Cookies (3PC) on Chrome as part of the Privacy Sandbox initiative.
+## Privacy Sandbox Analysis Tool CLI
 
+<p align="center">
+<img src="https://raw.githubusercontent.com/GoogleChromeLabs/ps-analysis-tool/main/packages/extension/icons/icon.svg" height="100" width="100">
+</p>
 
-## Using the Node CLI
+> The Privacy Sandbox Analysis Tool CLI is designed to help developers audit their websites for the upcoming deprecation of third-party cookies (3PCD) in Chrome as part of the Privacy Sandbox initiative.
 
-The PSAT CLI offers the most flexibility in configuration, execution, and reporting for maximum control and automation of third-party cookie audits. It's ideal for advanced users and automated workflows.
 
-> Note : PSAT CLI requires Node 18 LTS (18.x) or later.
+## Using the CLI
+
+PSAT CLI allows you to audit a single website or multiple websites using a sitemap or a CSV file, and generates a report that should help any user draft their future action items to prepare their website for third-party cookie depreciation.
+
+> Required Node Version: 18 or later
 
 
 ### Installation
@@ -14,7 +20,11 @@ The PSAT CLI offers the most flexibility in configuration, execution, and report
 npm i -g @google-psat/cli
 ```
 
-Run it : psat https://example.com
+### Usage
+
+```
+psat https://example.com
+```
 
 
 By default, PSAT generates the report as an HTML file. You can open it in a web browser of your choice and use it to filter the output.
@@ -24,10 +34,10 @@ By default, PSAT generates the report as an HTML file. You can open it in a web
 ```                                   
 Usage: psat [options]
 
-CLI to test a URL for 3p cookies
+CLI to test a URL for third-party cookies
 
 Arguments:
-  website-url                 The URL of website you want to analyse
+  url                         The URL of a website or sitemap you want to analyse.
 
 Options:
   -V, --version               output the version number
@@ -47,37 +57,40 @@ Options:
 ```
 
 ### Output
-After running the analysis, it will create a report in /out/ directory
+After running the analysis, it will create a report in `/out/` directory
 ```bash
 psat -u https://www.google.co.in
 ✓ Done analyzing cookies.
 ✓ Done analyzing technologies.
-Report created successfully: /Users/milind/projects/psat-cli-tool/out/www-google-co-in/index.html
+Report created successfully: /Users/username/projects/psat-cli-tool/out/www-google-co-in/index.html
 ```
 
-Open the HTML file generated in the browser
+Open the generated HTML file in the browser.
 
 ![PSAT CLI Output](https://s3.amazonaws.com/i.snag.gy/Dyzq6N.jpg)
 
 ## Docs
 
 - Learn more about the [Privacy Sandbox](https://privacysandbox.com/) initiative.
-- Please visit our [wiki](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/) to learn more about PSAT.
-- To understand the reports better, please check the [CLI output](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/PSAT-Command-Line-Interface#cli-output) section on our wiki.
-- To understand about cookie filters, check the [Cookie table](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/Cookies-Table) section on our wiki.
+- Visit our [wiki](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/) for more information about PSAT.
+- Please refer to the [CLI output](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/PSAT-Command-Line-Interface#cli-output) section on our wiki for a better understanding of the reports.
+- To learn about cookie filters, check the [Cookie table](https://github.com/GoogleChromeLabs/ps-analysis-tool/wiki/Cookies-Table) section on the wiki.
 
 
 ## Contributing
-We're excited to accept your code improvements (patches) and ideas for this project! Whether you want to be a regular contributor or just tackle an issue you care about, we value your input.
+We welcome your patches and contributions to this project. Whether you're a frequent contributor or addressing a specific issue that matters to you, we appreciate your input.
 
-To develop and contribute, Please check our [contribution guide](https://github.com/GoogleChromeLabs/ps-analysis-tool/blob/main/docs/CONTRIBUTING.md) for detailed information.
+To develop and contribute, please refer to our [contribution guide](https://github.com/GoogleChromeLabs/ps-analysis-tool/blob/main/docs/CONTRIBUTING.md) for detailed information.
 
 
 ## Using PSAT in Chrome extension
 
 PSAT is available as a Chrome extension that allows you to do an analysis while interacting with websites.
 
-Installation: You can install the PSAT extension from the [Chrome web store](https://chromewebstore.google.com/detail/privacy-sandbox-analysis/ehbnpceebmgpanbbfckhoefhdibijkef)
+### Installation: 
+
+You can install the PSAT extension from the [Chrome web store](https://chromewebstore.google.com/detail/privacy-sandbox-analysis/ehbnpceebmgpanbbfckhoefhdibijkef)
+
 To run it: Visit the website you want to analyze, open Chrome DevTools, and select the Privacy Sandbox panel.
 
 ![PSAT Chrome Extension](https://s3.amazonaws.com/i.snag.gy/OcCl5i.jpg)
\ No newline at end of file
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 4b11980e..36800a4b 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -19,7 +19,8 @@
     "unpublish:remote": "npm unpublish --registry=https://registry.npmjs.org --force"
   },
   "bin": {
-    "psat": "./dist/main.js"
+    "psat": "./dist/main.js",
+    "@google-psat/cli": "./dist/main.js"
   },
   "publishConfig": {
     "access": "public"
@@ -40,6 +41,7 @@
   "homepage": "https://github.com/GoogleChromeLabs/ps-analysis-tool",
   "dependencies": {
     "@google-psat/analysis-utils": "*",
+    "@google-psat/cli-dashboard": "*",
     "@google-psat/common": "*",
     "@google-psat/i18n": "*",
     "@google-psat/library-detection": "*",
@@ -58,8 +60,5 @@
     "tsc-watch": "^6.0.4",
     "typescript": "^5.0.4",
     "webpack": "^5.86.0"
-  },
-  "peerDependencies": {
-    "@google-psat/cli-dashboard": "*"
   }
 }
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index 0a53b50b..c3c821a8 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -19,7 +19,7 @@
  */
 import { Command } from 'commander';
 import events from 'events';
-import { ensureFile, writeFile } from 'fs-extra';
+import { existsSync, ensureFile, writeFile } from 'fs-extra';
 // @ts-ignore Package does not support typescript.
 import Spinnies from 'spinnies';
 import fs from 'fs';
@@ -103,15 +103,56 @@ const saveResultsAsHTML = async (
   result: CompleteJson | CompleteJson[],
   isSiteMap: boolean
 ) => {
-  const htmlText = fs.readFileSync(
-    path.resolve(__dirname + '../../../cli-dashboard/dist/index.html'),
-    'utf-8'
-  );
+  let htmlText = '';
+  let reportHTML = '';
+
+  if (
+    existsSync(
+      path.resolve(
+        __dirname +
+          '../../node_modules/@google-psat/cli-dashboard/dist/index.html'
+      )
+    )
+  ) {
+    htmlText = fs.readFileSync(
+      path.resolve(
+        __dirname +
+          '../../node_modules/@google-psat/cli-dashboard/dist/index.html'
+      ),
+      'utf-8'
+    );
 
-  const reportHTML = fs.readFileSync(
-    path.resolve(__dirname + '../../../cli-dashboard/dist/report/index.html'),
-    'base64'
-  );
+    reportHTML = fs.readFileSync(
+      path.resolve(
+        __dirname +
+          '../../node_modules/@google-psat/cli-dashboard/dist/report/index.html'
+      ),
+      'base64'
+    );
+
+    fs.copyFileSync(
+      path.resolve(
+        __dirname +
+          '../../node_modules/@google-psat/cli-dashboard/dist/index.js'
+      ),
+      outDir + '/index.js'
+    );
+  } else {
+    htmlText = fs.readFileSync(
+      path.resolve(__dirname + '../../../cli-dashboard/dist/index.html'),
+      'utf-8'
+    );
+
+    reportHTML = fs.readFileSync(
+      path.resolve(__dirname + '../../../cli-dashboard/dist/report/index.html'),
+      'base64'
+    );
+
+    fs.copyFileSync(
+      path.resolve(__dirname + '../../../cli-dashboard/dist/index.js'),
+      outDir + '/index.js'
+    );
+  }
 
   const messages = I18n.getMessages();
 
@@ -127,11 +168,6 @@ const saveResultsAsHTML = async (
     })}</script>` +
     htmlText.substring(htmlText.indexOf('</head>'));
 
-  fs.copyFileSync(
-    path.resolve(__dirname + '../../../cli-dashboard/dist/index.js'),
-    outDir + '/index.js'
-  );
-
   const outFileFullDir = path.resolve(outDir + '/index.html');
   const htmlBlob = new Blob([html]);
   const buffer = Buffer.from(await htmlBlob.arrayBuffer());
diff --git a/packages/i18n/src/i18n.ts b/packages/i18n/src/i18n.ts
index a96fd275..027ff754 100644
--- a/packages/i18n/src/i18n.ts
+++ b/packages/i18n/src/i18n.ts
@@ -139,24 +139,30 @@ class I18n {
     const localeArray = this.createLocaleArray(locale);
 
     for (const _locale of localeArray) {
+      let localePath = '';
       if (
         existsSync(
           path.resolve(
             __dirname +
-              `../../../i18n/_locales/messages/${_locale}/messages.json`
+              `../../node_modules/@google-psat/i18n/_locales/messages/${_locale}/messages.json`
           )
         )
       ) {
+        localePath = path.resolve(
+          __dirname +
+            `../../node_modules/@google-psat/i18n/_locales/messages/${_locale}/messages.json`
+        );
+      } else {
+        localePath = path.resolve(
+          __dirname + `../../../i18n/_locales/messages/${_locale}/messages.json`
+        );
+      }
+
+      if (existsSync(localePath)) {
         const messages = JSON.parse(
-          readFileSync(
-            path.resolve(
-              __dirname +
-                `../../../i18n/_locales/messages/${_locale}/messages.json`
-            ),
-            {
-              encoding: 'utf-8',
-            }
-          )
+          readFileSync(localePath, {
+            encoding: 'utf-8',
+          })
         );
 
         this.initMessages(messages);


Test patch:
diff --git a/packages/i18n/src/tests/i18n.ts b/packages/i18n/src/tests/i18n.ts
index 430fafd2..52e4859f 100644
--- a/packages/i18n/src/tests/i18n.ts
+++ b/packages/i18n/src/tests/i18n.ts
@@ -65,7 +65,6 @@ describe('I18n', () => {
 
     expect(result).toEqual(messages);
   });
-
   it('should load CLI messages data', async () => {
     const existsSyncMock = jest.spyOn(fs, 'existsSync');
     const readFileSyncMock = jest.spyOn(fs, 'readFileSync');
@@ -86,17 +85,14 @@ describe('I18n', () => {
     readFileSyncMock.mockReturnValueOnce(JSON.stringify(messages));
     await I18n.loadCLIMessagesData(locale);
 
-    expect(existsSyncMock).toHaveBeenCalledWith(
-      path.resolve(
-        __dirname + `../../../../i18n/_locales/messages/hi/messages.json`
-      )
+    expect(existsSyncMock).toHaveBeenNthCalledWith(
+      2,
+      path.resolve(__dirname + `../../../_locales/messages/hi/messages.json`)
     );
-    expect(existsSyncMock).toHaveBeenCalledTimes(1);
+    expect(existsSyncMock).toHaveBeenCalledTimes(2);
 
     expect(readFileSyncMock).toHaveBeenCalledWith(
-      path.resolve(
-        __dirname + `../../../../i18n/_locales/messages/hi/messages.json`
-      ),
+      path.resolve(__dirname + `../../../_locales/messages/hi/messages.json`),
       {
         encoding: 'utf-8',
       }