Repository: dora-team/dora.dev
Pull request #898: Add version tracker for gen AI guide
Author: login: nathenharvey
Created: 2025-03-05T15:20:01Z | Merged: 2025-03-06T19:48:58Z

Resolves #862


Preview URLs:

* https://doradotdev--pr898-drafts-off-m44rwj7k.web.app/vc/genai/?v=2025.1 - the current version
* https://doradotdev--pr898-drafts-off-m44rwj7k.web.app/vc/genai/?v=2024.1 - a non-existent version
* https://doradotdev--pr898-drafts-off-m44rwj7k.web.app/vc/genai/?v= - no version specified

Linked issue #862: Add version tracker for gen AI guide

Diff:
diff --git a/hugo/content/vc/_index.md b/hugo/content/vc/_index.md
index 2f7cc7e13..24093d939 100644
--- a/hugo/content/vc/_index.md
+++ b/hugo/content/vc/_index.md
@@ -4,6 +4,7 @@ date: 2024-10-17
 draft: false
 bannerTitle: "DORA Report Version Check"
 bannerSubtitle: "Check if you have the most recent version of the DORA Report."
+layout: single
 ---
 
 <style>
@@ -135,41 +136,12 @@ bannerSubtitle: "Check if you have the most recent version of the DORA Report."
   <a href="/research/2024/dora-report"><img src="/research/2024/dora-report/2024-dora-accelerate-state-of-devops-report.png" alt="2024 DORA Report Cover" style="max-width:12em;"></a>
 </div>
 
+<script src="/js/version-check-utils.js"></script>
 <script>
-  function getParameterByName(name, url = window.location.href) {
-    name = name.replace(/[\[\]]/g, '\$&');
-    var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
-        results = regex.exec(url);
-    if (!results) return null;
-    if (!results[2]) return '';
-    return decodeURIComponent(results[2].replace(/\+/g, ' '));
-  }
-
-  function showContent(version) {
-    // Show content for exact version match
-    document.querySelectorAll(`.version-content[data-version="${version}"]`).forEach(el => el.style.display = 'block');
-
-    // Show content for version prefix match
-    document.querySelectorAll(`.version-content[data-version-prefix^="${version.substring(0, 4)}-"]`).forEach(el => {
-      // Hide if excluded version
-      if (el.dataset.exclude && el.dataset.exclude.includes(version)) {
-        return;
-      }
-      el.style.display = 'block';
-    });
-
-      // Show default content if no version match
-      const defaultContent = document.querySelector('.version-content[data-default]');
-      if (defaultContent && !document.querySelector('.version-content[style="display: block;"]')) {
-        defaultContent.style.display = 'block';
-      }
-  }
-
   const version = getParameterByName('v');
   if (version) {
     showContent(version);
   } else {
-    // Show default content if no version is specified or undefined
-    document.querySelector('.version-content[data-default]').style.display = 'block';
+     showContent();
   }
 </script>
diff --git a/hugo/content/vc/genai/index.md b/hugo/content/vc/genai/index.md
new file mode 100644
index 000000000..f6fde7c8a
--- /dev/null
+++ b/hugo/content/vc/genai/index.md
@@ -0,0 +1,48 @@
+---
+title: "Impact of Generative AI on Software Development Version Check"
+date: 2024-10-17
+draft: false
+bannerTitle: "Impact of Generative AI on Software Development"
+bannerSubtitle: "Check if you have the most recent version of the report."
+---
+
+<style>
+  .version-content {
+    display: none;
+  }
+</style>
+
+<div class="version-content" data-default>
+  <h2><span class="google-material-icons" style="color: orange; font-size:1em;">warning</span>Unrecognized version</h2>
+  <p>We do not recognize that version of the Impact of Generative AI on Software Development report.</p>
+
+  <h3>Known versions</h3>
+  <p>The following versions of the Impact of Generative AI on Software Development report are available via this version checker:</p>
+  <ul>
+    <li>
+      <span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span> <a href="/vc/genai/?v=2025.1">Impact of Generative AI on Software Development <code>v. 2025.1</code></a>
+    </li>
+  </ul>
+</div>
+
+<!-- version is 2025.1 -->
+<div class="version-content" data-version="2025.1">
+  <h2><span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span>Impact of Generative AI on Software Development</h2>
+  <p>
+    You have the most recent version of the Impact of Generative AI on Software Development report.
+  </p>
+  <p>
+    Your version: <code>v.2025.1</code><br />
+    Latest version: <code>v.2025.1</code>
+  </p>
+</div>
+
+<script src="/js/version-check-utils.js"></script>
+<script>
+  const version = getParameterByName('v');
+  if (version) {
+    showContent(version);
+  } else {
+     showContent();
+  }
+</script>
diff --git a/hugo/static/js/version-check-utils.js b/hugo/static/js/version-check-utils.js
new file mode 100644
index 000000000..fa3bd5b7a
--- /dev/null
+++ b/hugo/static/js/version-check-utils.js
@@ -0,0 +1,22 @@
+function getParameterByName(name, url = window.location.href) {
+  name = name.replace(/[\[\]]/g, '\$&'); // escape square brackets in name for regex
+  const urlObject = new URL(url);
+  return urlObject.searchParams.get(name);
+}
+
+function showContent(version) {
+  // Show content for exact version match
+  document.querySelectorAll(`.version-content[data-version="${version}"]`).forEach(el => {
+    if (el) {
+      el.style.display = 'block';
+    }
+  });
+
+  // Show default content if no version match
+  const defaultContent = document.querySelector('.version-content[data-default]');
+  if (defaultContent && !document.querySelector('.version-content[style="display: block;"]')) {
+    if (defaultContent) {
+      defaultContent.style.display = 'block';
+    }
+  }
+}
diff --git a/test/playwright/tests/vc/genai-version-check.spec.ts b/test/playwright/tests/vc/genai-version-check.spec.ts
new file mode 100644
index 000000000..2fd2f7bf2
--- /dev/null
+++ b/test/playwright/tests/vc/genai-version-check.spec.ts
@@ -0,0 +1,73 @@
+import { test, expect } from '@playwright/test';
+
+const versions = [
+  { version: '2025.1', expectedText: 'Impact of Generative AI on Software Development' },
+];
+
+versions.forEach(({ version, expectedText }) => {
+  test(`Gen AI version checker recognizes v ${version}`, async ({ page }) => {
+    await page.goto(`/vc/genai/?v=${version}`);
+
+    // Check the correct version is displayed
+    await expect(page.locator(`div[data-version="${version}"]`)).toBeVisible();
+
+    // Check other versions are hidden
+    versions
+      .filter((v) => v.version !== version)
+      .forEach(async ({ version }) => {
+        await expect(page.locator(`div[data-version="${version}"]`)).toBeHidden();
+      });
+
+    // Check "Unrecognized version" is hidden
+    await expect(page.locator('h2', { hasText: 'Unrecognized version' })).toBeHidden();
+  });
+});
+
+const invalidVersions = ['', 'random', '123-456', '123.456'];
+
+invalidVersions.forEach((version) => {
+  test(`Gen AI version checker handles ${version ? `'${version}'` : 'empty'} version`, async ({ page }) => {
+    await page.goto(`/vc/genai/?v=${version}`);
+
+    // Check all known versions are hidden
+    versions.forEach(async ({ version }) => {
+      await expect(page.locator(`div[data-version="${version}"]`)).toBeHidden();
+    });
+
+    // Check "Unrecognized version" is visible
+    await expect(page.locator('h2', { hasText: 'Unrecognized version' })).toBeVisible();
+  });
+});
+
+test('Gen AI version checker handles extra data in the query string', async ({ page }) => {
+  const randomNumber = Math.floor(Math.random() * 1000) + 1;
+  const randomNumber2 = Math.floor(Math.random() * 1000) + 1;
+  await page.goto(`/vc/genai/?foo=${randomNumber}&bar=${randomNumber2}&v=2025.1`);
+
+  // Check the correct version is displayed
+  await expect(page.locator('div[data-version="2025.1"]')).toBeVisible();
+
+  // Check other versions are hidden
+  versions
+    .filter((v) => v.version !== '2025.1')
+    .forEach(async ({ version }) => {
+      await expect(page.locator(`div[data-version="${version}"]`)).toBeHidden();
+    });
+
+  // Check "Unrecognized version" is hidden
+  await expect(page.locator('h2', { hasText: 'Unrecognized version' })).toBeHidden();
+});
+
+// No version specified
+test('Gen AI version checker has the correct title.', async ({ page }) => {
+  await page.goto('/vc/genai/');
+
+  await expect(page).toHaveTitle('DORA | Impact of Generative AI on Software Development Version Check');
+});
+
+// 2025.1 version
+test('Gen AI version checker has the correct header.', async ({ page }) => {
+  await page.goto('/vc/genai/?v=2025.1');
+
+  await expect(page.locator('h1')).toContainText('Impact of Generative AI on Software Development');
+});


Reviews:
user: login: gemini-code-assist[bot]
body: Hello @nathenharvey, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

## Summary of Changes
This pull request adds a version tracker for the Generative AI guide. It introduces a new page that checks the version of the "Impact of Generative AI on Software Development" report based on a query parameter. The page displays a message indicating whether the user has the most recent version or an unrecognized version. It also includes Playwright tests to ensure the version checker functions correctly.

### Highlights
* **Version Tracking**: Implements version tracking for the Generative AI guide using a query parameter to determine the report version.
* **Dynamic Content Display**: Dynamically displays content based on the version parameter, showing either the latest version confirmation or an unrecognized version message.
* **Playwright Tests**: Adds Playwright tests to verify the version checker's functionality, including handling valid, invalid, and missing version parameters.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **hugo/content/vc/genai/_index.md**
  * Created a new file `_index.md` to implement the version checker page.
  * Added metadata including title, date, and banner information.
  * Implemented HTML and CSS to display version-specific content.
  * Included JavaScript to parse the version from the query parameter and display the appropriate content.
  * Added a default state to handle unrecognized or missing versions.
* **test/playwright/tests/vc/genai-version-check.spec.ts**
  * Created a new test file to verify the version checker's functionality.
  * Added tests to check the display of content for valid versions (e.g., '2025.1').
  * Added tests to check the handling of invalid versions (e.g., '', 'random', '123-456', '123.456').
  * Added tests to ensure the version checker handles extra data in the query string.
  * Added tests to verify the page title and header content.

</details>


<details>

<summary><b>Using Gemini Code Assist</b></summary>
<br>

The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.

<b>Invoking Gemini</b>

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands.

Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments.
Help | `/gemini help` | Displays a list of available commands.

<b>Customization</b>

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/review-github-code).

<b>Limitations & Feedback</b>

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments to provide feedback.

</details>

<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>


[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/review-github-code). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).

---

> A query string's tale,
> A version to unveil,
> With tests so keen,
> The truth is seen,
> Is your report up-to-date, or frail?


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T15:20:12Z
user: login: gemini-code-assist[bot]
body: ## Code Review
This pull request introduces a version checker for the Impact of Generative AI on Software Development report. The implementation uses JavaScript to read a version parameter from the URL and display the appropriate content. Playwright tests have also been added to verify the functionality. Overall, the code is well-structured and addresses the issue effectively.

### Merge Readiness
The code is well-structured and includes comprehensive tests. I don't see any critical or high severity issues. I am unable to directly approve the pull request, and recommend that others review and approve this code before merging.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T15:20:21Z
user: login: gemini-code-assist[bot]
body: ## Code Review
The pull request introduces a version checker for the Generative AI guide, which is a useful feature for ensuring users have the most up-to-date information. The implementation involves adding a new content page with version-specific content and updating the existing DORA report version checker to use a shared JavaScript utility. Playwright tests have also been added to verify the functionality.

### Merge Readiness
The pull request appears to be well-structured and includes appropriate testing. I am unable to directly approve the pull request, and recommend that others review and approve this code before merging. There are no critical or high severity issues, so the pull request is in good shape to be merged.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T15:59:32Z
user: login: davidstanke
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-06T19:48:51Z

Review comments:
diff_hunk: @@ -0,0 +1,68 @@
+---
+title: "Impact of Generative AI on Software Development Version Check"
+date: 2024-10-17
+draft: false
+bannerTitle: "Impact of Generative AI on Software Development"
+bannerSubtitle: "Check if you have the most recent version of the report."
+---
+
+<style>
+  .version-content {
+    display: none;
+  }
+</style>
+
+<div class="version-content" data-default>
+  <h2><span class="google-material-icons" style="color: orange; font-size:1em;">warning</span>Unrecognized version</h2>
+  <p>We do not recognize that version of the Impact of Generative AI on Software Development report.</p>
+
+  <h3>Known versions</h3>
+  <p>The following versions of the Impact of Generative AI on Software Development report are available via this version checker:</p>
+  <ul>
+    <li>
+      <span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span> <a href="/vc/genai/?v=2025.1">Impact of Generative AI on Software Development <code>v. 2025.1</code></a>
+    </li>
+  </ul>
+</div>
+
+<!-- version is 2025.1 -->
+<div class="version-content" data-version="2025.1">
+  <h2><span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span>Impact of Generative AI on Software Development</h2>
+  <p>
+    You have the most recent version of the Impact of Generative AI on Software Development report.
+  </p>
+  <p>
+    Your version: <code>v.2025.1</code><br />
+    Latest version: <code>v.2025.1</code>
+  </p>
+</div>
+
+<script>
+  function getParameterByName(name, url = window.location.href) {
+    name = name.replace(/[\[\]]/g, '\$&');
+    var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
+        results = regex.exec(url);
+    if (!results) return null;
+    if (!results[2]) return '';
+    return decodeURIComponent(results[2].replace(/\+/g, ' '));
+  }
path: hugo/content/vc/genai/_index.md
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `getParameterByName` function could be simplified by using the URLSearchParams API, which is widely supported in modern browsers. This would make the code more readable and maintainable. Consider refactoring to use this API.

```suggestion
  function getParameterByName(name, url = window.location.href) {
    const urlParams = new URLSearchParams(url);
    return urlParams.get(name);
  }
```
created_at: 2025-03-05T15:20:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,68 @@
+---
+title: "Impact of Generative AI on Software Development Version Check"
+date: 2024-10-17
+draft: false
+bannerTitle: "Impact of Generative AI on Software Development"
+bannerSubtitle: "Check if you have the most recent version of the report."
+---
+
+<style>
+  .version-content {
+    display: none;
+  }
+</style>
+
+<div class="version-content" data-default>
+  <h2><span class="google-material-icons" style="color: orange; font-size:1em;">warning</span>Unrecognized version</h2>
+  <p>We do not recognize that version of the Impact of Generative AI on Software Development report.</p>
+
+  <h3>Known versions</h3>
+  <p>The following versions of the Impact of Generative AI on Software Development report are available via this version checker:</p>
+  <ul>
+    <li>
+      <span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span> <a href="/vc/genai/?v=2025.1">Impact of Generative AI on Software Development <code>v. 2025.1</code></a>
+    </li>
+  </ul>
+</div>
+
+<!-- version is 2025.1 -->
+<div class="version-content" data-version="2025.1">
+  <h2><span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span>Impact of Generative AI on Software Development</h2>
+  <p>
+    You have the most recent version of the Impact of Generative AI on Software Development report.
+  </p>
+  <p>
+    Your version: <code>v.2025.1</code><br />
+    Latest version: <code>v.2025.1</code>
+  </p>
+</div>
+
+<script>
+  function getParameterByName(name, url = window.location.href) {
+    name = name.replace(/[\[\]]/g, '\$&');
+    var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
+        results = regex.exec(url);
+    if (!results) return null;
+    if (!results[2]) return '';
+    return decodeURIComponent(results[2].replace(/\+/g, ' '));
+  }
+
+  function showContent(version) {
+    // Show content for exact version match
+    document.querySelectorAll(`.version-content[data-version="${version}"]`).forEach(el => el.style.display = 'block');
+
path: hugo/content/vc/genai/_index.md
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider adding a check to ensure that `el` is not null before accessing its `style` property. This would prevent potential errors if the selector doesn't find any matching elements.

```suggestion
    document.querySelectorAll(`.version-content[data-version="${version}"]`).forEach(el => {
      if (el) {
        el.style.display = 'block';
      }
    });
```
created_at: 2025-03-05T15:20:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,68 @@
+---
+title: "Impact of Generative AI on Software Development Version Check"
+date: 2024-10-17
+draft: false
+bannerTitle: "Impact of Generative AI on Software Development"
+bannerSubtitle: "Check if you have the most recent version of the report."
+---
+
+<style>
+  .version-content {
+    display: none;
+  }
+</style>
+
+<div class="version-content" data-default>
+  <h2><span class="google-material-icons" style="color: orange; font-size:1em;">warning</span>Unrecognized version</h2>
+  <p>We do not recognize that version of the Impact of Generative AI on Software Development report.</p>
+
+  <h3>Known versions</h3>
+  <p>The following versions of the Impact of Generative AI on Software Development report are available via this version checker:</p>
+  <ul>
+    <li>
+      <span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span> <a href="/vc/genai/?v=2025.1">Impact of Generative AI on Software Development <code>v. 2025.1</code></a>
+    </li>
+  </ul>
+</div>
+
+<!-- version is 2025.1 -->
+<div class="version-content" data-version="2025.1">
+  <h2><span class="google-material-icons" style="color: green; font-size:1em;">check_circle</span>Impact of Generative AI on Software Development</h2>
+  <p>
+    You have the most recent version of the Impact of Generative AI on Software Development report.
+  </p>
+  <p>
+    Your version: <code>v.2025.1</code><br />
+    Latest version: <code>v.2025.1</code>
+  </p>
+</div>
+
+<script>
+  function getParameterByName(name, url = window.location.href) {
+    name = name.replace(/[\[\]]/g, '\$&');
+    var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
+        results = regex.exec(url);
+    if (!results) return null;
+    if (!results[2]) return '';
+    return decodeURIComponent(results[2].replace(/\+/g, ' '));
+  }
+
+  function showContent(version) {
+    // Show content for exact version match
+    document.querySelectorAll(`.version-content[data-version="${version}"]`).forEach(el => el.style.display = 'block');
+
+    // Show default content if no version match
+    const defaultContent = document.querySelector('.version-content[data-default]');
+    if (defaultContent && !document.querySelector('.version-content[style="display: block;"]')) {
+      defaultContent.style.display = 'block';
+    }
path: hugo/content/vc/genai/_index.md
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Similar to the previous comment, consider adding a check to ensure that `defaultContent` is not null before accessing its `style` property.

```suggestion
    if (defaultContent && !document.querySelector('.version-content[style="display: block;"]')) {
      if (defaultContent) {
        defaultContent.style.display = 'block';
      }
    }
```
created_at: 2025-03-05T15:20:21Z
author_association: CONTRIBUTOR

PR comments:
user: login: nathenharvey
created_at: 2025-03-05T15:59:19Z
body: /gemini review

author_association: COLLABORATOR
user: login: dora-machine-user
created_at: 2025-03-05T16:52:14Z
body: Cloud Build time: Thu Mar  6 19:42:31 UTC 2025
Preview this PR [with drafts on] at https://doradotdev--pr898-drafts-on-ivpvlu9n.web.app
Preview this PR [with drafts off] at https://doradotdev--pr898-drafts-off-m44rwj7k.web.app

author_association: COLLABORATOR