Repository: marp-team/marp-vscode (ts)

Issue:
`markdown.marp.pdf.outlines` option to generate outlines in PDF
[Marp CLI v2.2.0](https://github.com/marp-team/marp-cli/releases/tag/v2.2.0) has new `--pdf-outlines` option to generate outlines in output PDF from slide pages and Markdown headings. When bumping dependency of Marp CLI, it is better to add corresponding configuration `markdown.marp.pdf.outlines`.

- **`markdown.marp.pdf.outlines`**
  - `off`: No outlines added to PDF. (default)
  - `pages`: Outlines based on slide pages will add to PDF. (`--pdf-outlines --pdf-outlines.pages --pdf-outlines.headings=false`)
  - `headings`: Outlines based on Markdown headings will add to PDF. (`--pdf-outlines --pdf-outlines.pages=false --pdf-outlines.headings`)
  - `both`: Outlines based on both of slide pages and Markdown headings will add to PDF. (`--pdf-outlines`)

Pull request description:
Add `markdown.marp.pdf.outlines` option
Resolves #381.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de39bdd..eaa7944 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,10 +4,14 @@
 
 ## [Unreleased]
 
+### Added
+
+- `markdown.marp.pdf.outlines` option to generate outlines in exported PDF ([#381](https://github.com/marp-team/marp-vscode/issues/381), [#384](https://github.com/marp-team/marp-vscode/pull/384))
+
 ### Changed
 
 - Upgrade Marp Core to [v3.3.3](https://github.com/marp-team/marp-core/releases/tag/v3.3.3) ([#378](https://github.com/marp-team/marp-vscode/pull/378))
-- Upgrade Marp CLI to [v2.1.4](https://github.com/marp-team/marp-cli/releases/tag/v2.1.4) ([#380](https://github.com/marp-team/marp-vscode/pull/380))
+- Upgrade Marp CLI to [v2.2.0](https://github.com/marp-team/marp-cli/releases/tag/v2.2.0) ([#384](https://github.com/marp-team/marp-vscode/pull/384))
 
 ## v2.2.1 - 2022-08-12
 
diff --git a/package-lock.json b/package-lock.json
index 4725958..57b7ea4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
       "version": "2.2.1",
       "license": "MIT",
       "dependencies": {
-        "@marp-team/marp-cli": "^2.1.4"
+        "@marp-team/marp-cli": "^2.2.0"
       },
       "devDependencies": {
         "@babel/preset-env": "^7.18.10",
@@ -60,6 +60,7 @@
         "typescript": "^4.7.4",
         "unified": "^10.1.2",
         "unist-util-visit": "^4.1.0",
+        "util": "^0.12.4",
         "vsce": "^2.10.0",
         "vscode-uri": "^3.0.3",
         "webpack": "^5.74.0",
@@ -2672,9 +2673,9 @@
       }
     },
     "node_modules/@marp-team/marp-cli": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@marp-team/marp-cli/-/marp-cli-2.1.4.tgz",
-      "integrity": "sha512-g0E4ZfLdn/RKEy0n+ZPF7NmEyCS7yeyFO0DkgDoADf/MKSWsZQjD7YucweMH9GINaQw/QJ0iSqTpvxXYngz3fg==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@marp-team/marp-cli/-/marp-cli-2.2.0.tgz",
+      "integrity": "sha512-AcU2hev/p1F+lk1bdMxxtafv0vGCYWaXE8FPuCPO3E55O84vN17Z5dapD8uolYAHOnJMQ06geAJ6cCAqO4J2eg==",
       "dependencies": {
         "@marp-team/marp-core": "^3.3.3",
         "@marp-team/marpit": "^2.4.1",
@@ -2682,7 +2683,7 @@
         "cosmiconfig": "^7.0.1",
         "import-from": "^4.0.0",
         "is-wsl": "^2.2.0",
-        "puppeteer-core": "17.1.3",
+        "puppeteer-core": "18.0.3",
         "serve-index": "^1.9.1",
         "tmp": "^0.2.1",
         "v8-compile-cache": "^2.3.0",
@@ -4243,6 +4244,18 @@
       "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
       "dev": true
     },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/azure-devops-node-api": {
       "version": "11.2.0",
       "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz",
@@ -8396,6 +8409,15 @@
       "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
       "dev": true
     },
+    "node_modules/for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+      "dev": true,
+      "dependencies": {
+        "is-callable": "^1.1.3"
+      }
+    },
     "node_modules/form-data": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -9395,6 +9417,22 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -9762,6 +9800,25 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-typed-array": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz",
+      "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==",
+      "dev": true,
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "es-abstract": "^1.20.0",
+        "for-each": "^0.3.3",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-typedarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -14900,9 +14957,9 @@
       }
     },
     "node_modules/puppeteer-core": {
-      "version": "17.1.3",
-      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-17.1.3.tgz",
-      "integrity": "sha512-gm3d5fTVEc+h7jVtT3Y1k8OL1awaOzln44UfsJhUJKH/tyO/wn/zOxyAHTQt9aX/yo37IS0dfisU3i3P8qvZnw==",
+      "version": "18.0.3",
+      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-18.0.3.tgz",
+      "integrity": "sha512-QBCr4zX93WhaXOeznj/XmfTrmz0H8Die2NnOKdLHxqGhFu0HipgKiuKUktVGm8R5mCCJoi1HFbnjOwKgO8P4dQ==",
       "dependencies": {
         "cross-fetch": "3.1.5",
         "debug": "4.3.4",
@@ -17817,6 +17874,20 @@
       "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
       "dev": true
     },
+    "node_modules/util": {
+      "version": "0.12.4",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz",
+      "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==",
+      "dev": true,
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "is-arguments": "^1.0.4",
+        "is-generator-function": "^1.0.7",
+        "is-typed-array": "^1.1.3",
+        "safe-buffer": "^5.1.2",
+        "which-typed-array": "^1.1.2"
+      }
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -18354,6 +18425,26 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/which-typed-array": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz",
+      "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==",
+      "dev": true,
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "es-abstract": "^1.20.0",
+        "for-each": "^0.3.3",
+        "has-tostringtag": "^1.0.0",
+        "is-typed-array": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/wicked-good-xpath": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz",
@@ -20631,9 +20722,9 @@
       }
     },
     "@marp-team/marp-cli": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@marp-team/marp-cli/-/marp-cli-2.1.4.tgz",
-      "integrity": "sha512-g0E4ZfLdn/RKEy0n+ZPF7NmEyCS7yeyFO0DkgDoADf/MKSWsZQjD7YucweMH9GINaQw/QJ0iSqTpvxXYngz3fg==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@marp-team/marp-cli/-/marp-cli-2.2.0.tgz",
+      "integrity": "sha512-AcU2hev/p1F+lk1bdMxxtafv0vGCYWaXE8FPuCPO3E55O84vN17Z5dapD8uolYAHOnJMQ06geAJ6cCAqO4J2eg==",
       "requires": {
         "@marp-team/marp-core": "^3.3.3",
         "@marp-team/marpit": "^2.4.1",
@@ -20641,7 +20732,7 @@
         "cosmiconfig": "^7.0.1",
         "import-from": "^4.0.0",
         "is-wsl": "^2.2.0",
-        "puppeteer-core": "17.1.3",
+        "puppeteer-core": "18.0.3",
         "serve-index": "^1.9.1",
         "tmp": "^0.2.1",
         "v8-compile-cache": "^2.3.0",
@@ -21911,6 +22002,12 @@
       "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
       "dev": true
     },
+    "available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "dev": true
+    },
     "azure-devops-node-api": {
       "version": "11.2.0",
       "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz",
@@ -24833,6 +24930,15 @@
       "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
       "dev": true
     },
+    "for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+      "dev": true,
+      "requires": {
+        "is-callable": "^1.1.3"
+      }
+    },
     "form-data": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -25563,6 +25669,16 @@
       "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
       "dev": true
     },
+    "is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      }
+    },
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -25793,6 +25909,19 @@
         "has-symbols": "^1.0.2"
       }
     },
+    "is-typed-array": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz",
+      "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==",
+      "dev": true,
+      "requires": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "es-abstract": "^1.20.0",
+        "for-each": "^0.3.3",
+        "has-tostringtag": "^1.0.0"
+      }
+    },
     "is-typedarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -29566,9 +29695,9 @@
       }
     },
     "puppeteer-core": {
-      "version": "17.1.3",
-      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-17.1.3.tgz",
-      "integrity": "sha512-gm3d5fTVEc+h7jVtT3Y1k8OL1awaOzln44UfsJhUJKH/tyO/wn/zOxyAHTQt9aX/yo37IS0dfisU3i3P8qvZnw==",
+      "version": "18.0.3",
+      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-18.0.3.tgz",
+      "integrity": "sha512-QBCr4zX93WhaXOeznj/XmfTrmz0H8Die2NnOKdLHxqGhFu0HipgKiuKUktVGm8R5mCCJoi1HFbnjOwKgO8P4dQ==",
       "requires": {
         "cross-fetch": "3.1.5",
         "debug": "4.3.4",
@@ -31795,6 +31924,20 @@
       "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
       "dev": true
     },
+    "util": {
+      "version": "0.12.4",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz",
+      "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "is-arguments": "^1.0.4",
+        "is-generator-function": "^1.0.7",
+        "is-typed-array": "^1.1.3",
+        "safe-buffer": "^5.1.2",
+        "which-typed-array": "^1.1.2"
+      }
+    },
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -32194,6 +32337,20 @@
         "is-symbol": "^1.0.3"
       }
     },
+    "which-typed-array": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz",
+      "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==",
+      "dev": true,
+      "requires": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "es-abstract": "^1.20.0",
+        "for-each": "^0.3.3",
+        "has-tostringtag": "^1.0.0",
+        "is-typed-array": "^1.1.9"
+      }
+    },
     "wicked-good-xpath": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz",
diff --git a/package.json b/package.json
index 26f6593..6d22c7b 100644
--- a/package.json
+++ b/package.json
@@ -172,10 +172,27 @@
           "default": false,
           "markdownDescription": "Adds [presenter notes](https://marpit.marp.app/usage?id=presenter-notes) to exported PDF as note annotations."
         },
+        "markdown.marp.pdf.outlines": {
+          "type": "string",
+          "enum": [
+            "off",
+            "pages",
+            "headings",
+            "both"
+          ],
+          "default": "off",
+          "description": "Adds outlines (bookmarks) to exported PDF. It is useful for navigation in PDF viewer.",
+          "enumDescriptions": [
+            "Disable outlines.",
+            "Add outlines based on each slide page.",
+            "Add outlines based on each Markdown heading.",
+            "Add outlines based on both slide pages and Markdown headings."
+          ]
+        },
         "markdown.marp.strictPathResolutionDuringExport": {
           "type": "boolean",
           "default": false,
-          "markdownDescription": "_[Experimental]_ Enables strict path resolution during export. If enabled, the export command tries to resolve relative paths from VS Code workspace that a Markdown file belongs. If not, or the Markdown does not belong to any workspace, the export command resolves paths based on the local file system."
+          "markdownDescription": "_[Experimental]_ Enables strict path resolution during export. If enabled, the export command tries to resolve relative paths from VS Code workspace that a Markdown file belongs. If disabled, or the Markdown does not belong to any workspace, the export command resolves paths based on the local file system."
         },
         "markdown.marp.themes": {
           "type": "array",
@@ -314,6 +331,7 @@
     "typescript": "^4.7.4",
     "unified": "^10.1.2",
     "unist-util-visit": "^4.1.0",
+    "util": "^0.12.4",
     "vsce": "^2.10.0",
     "vscode-uri": "^3.0.3",
     "webpack": "^5.74.0",
@@ -321,6 +339,6 @@
     "yaml": "^2.1.1"
   },
   "dependencies": {
-    "@marp-team/marp-cli": "^2.1.4"
+    "@marp-team/marp-cli": "^2.2.0"
   }
 }
diff --git a/src/__mocks__/vscode.ts b/src/__mocks__/vscode.ts
index 2176f68..0b9d12e 100644
--- a/src/__mocks__/vscode.ts
+++ b/src/__mocks__/vscode.ts
@@ -10,6 +10,7 @@ const defaultConf: MockedConf = {
   'markdown.marp.exportType': 'pdf',
   'markdown.marp.outlineExtension': true,
   'markdown.marp.pdf.noteAnnotations': false,
+  'markdown.marp.pdf.outlines': 'off',
   'window.zoomLevel': 0,
 }
 
diff --git a/src/option.ts b/src/option.ts
index 73c9648..36e5788 100644
--- a/src/option.ts
+++ b/src/option.ts
@@ -38,6 +38,19 @@ const math = () => {
   return conf === 'off' ? false : conf
 }
 
+const pdfOutlines = () => {
+  switch (marpConfiguration().get<string>('pdf.outlines')) {
+    case 'pages':
+      return { pages: true, headings: false }
+    case 'headings':
+      return { pages: false, headings: true }
+    case 'both':
+      return { pages: true, headings: true }
+    default:
+      return false
+  }
+}
+
 export const marpCoreOptionForPreview = (
   baseOption: Options & MarpOptions
 ): MarpOptions => {
@@ -73,6 +86,7 @@ export const marpCoreOptionForCLI = async (
   const baseOpts = {
     allowLocalFiles,
     pdfNotes,
+    pdfOutlines: pdfOutlines(),
     html: enableHtml() || undefined,
     options: {
       markdown: {
diff --git a/webpack.web.config.js b/webpack.web.config.js
index d0e2ea9..764c15a 100644
--- a/webpack.web.config.js
+++ b/webpack.web.config.js
@@ -25,6 +25,7 @@ module.exports = (env) => {
         // Node.js polyfills
         os: require.resolve('os-browserify/browser'),
         path: require.resolve('path-browserify'),
+        util: require.resolve('util/'),
       },
     },
     plugins: [


Test patch:
diff --git a/src/option.test.ts b/src/option.test.ts
index 924154c..a21eeae 100644
--- a/src/option.test.ts
+++ b/src/option.test.ts
@@ -85,6 +85,31 @@ describe('Option', () => {
       ).toBe(false)
     })
 
+    it('sets pdfOutlines option in response to the preference', async () => {
+      const pdfOutlines = async () =>
+        (await subject({ uri: untitledUri })).pdfOutlines
+
+      expect(await pdfOutlines()).toBe(false)
+
+      setConfiguration({ 'markdown.marp.pdf.outlines': 'pages' })
+      expect(await pdfOutlines()).toStrictEqual({
+        pages: true,
+        headings: false,
+      })
+
+      setConfiguration({ 'markdown.marp.pdf.outlines': 'headings' })
+      expect(await pdfOutlines()).toStrictEqual({
+        pages: false,
+        headings: true,
+      })
+
+      setConfiguration({ 'markdown.marp.pdf.outlines': 'both' })
+      expect(await pdfOutlines()).toStrictEqual({
+        pages: true,
+        headings: true,
+      })
+    })
+
     describe('when targeted document belongs to workspace', () => {
       const css = '/* @theme test */'