diff --git a/.github/workflows/deploy-v2.yml b/.github/workflows/deploy-v2.yml
new file mode 100644
index 0000000000000..1c24a890aad63
--- /dev/null
+++ b/.github/workflows/deploy-v2.yml
@@ -0,0 +1,58 @@
+name: Deploy v2 preview
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ build-and-deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: actions/checkout@v2
+ with:
+ repository: microsoft/playwright
+ path: v2/content-repo
+ fetch-depth: 0
+ # The same token is used by the commit action later on
+ token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
+
+ - uses: actions/setup-node@v1
+ with:
+ node-version: '12'
+ - run: npm install
+ working-directory: v2
+
+ - name: Fetch content (master)
+ run: node scripts/fetchContent.js
+ working-directory: v2
+ env:
+ SRC_DIR: content-repo/docs
+ - name: Fetch content (v1.6.1)
+ run: node scripts/fetchContent.js
+ working-directory: v2
+ env:
+ SRC_DIR: content-repo/docs
+ VERSION: '1.6.1'
+
+ - run: npm run build
+ working-directory: v2
+
+ # Replace gh-pages branch of content-repo
+ # with the new build
+ - run: git checkout gh-pages
+ working-directory: v2/content-repo
+ - name: Clean existing files
+ run: rm -rf *
+ working-directory: v2/content-repo
+ - run: cp -a build/. content-repo/
+ - run: git status
+ working-directory: content-repo
+
+ # Push to gh-pages branch of microsoft/playwright
+ - uses: stefanzweifel/git-auto-commit-action@v4
+ with:
+ branch: gh-pages
+ repository: v2/content-repo
diff --git a/v2/.gitignore b/v2/.gitignore
new file mode 100644
index 0000000000000..1d580cdca6789
--- /dev/null
+++ b/v2/.gitignore
@@ -0,0 +1,28 @@
+# dependencies
+/node_modules
+
+# production
+/build
+
+# generated files
+.docusaurus
+.cache-loader
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# contents (generated by scripts)
+docs/*
+!docs/.gitkeep
+versioned_docs/*
+versioned_sidebars/*
+versions.json
+sidebars.js
diff --git a/v2/README.md b/v2/README.md
new file mode 100644
index 0000000000000..5efe807baaa2d
--- /dev/null
+++ b/v2/README.md
@@ -0,0 +1,37 @@
+# playwright.dev-v2
+
+This website is built using [Docusaurus v2](https://v2.docusaurus.io/). Content is pulled from the microsoft/playwright repo.
+
+## Development
+
+```sh
+npm install
+npm start
+```
+
+### Fetch docs content
+
+1. Clone the content repo (microsoft/playwright) on your disk
+1. To setup content from master (`next` version)
+
+ ```sh
+ SRC_DIR=path-to-playwright-repo/docs node scripts/fetchContent.js
+ ```
+
+1. To setup content for a particular version (say v1.6.0)
+
+ ```sh
+ SRC_DIR=path-to-playwright-repo/docs VERSION=1.6.0 node scripts/fetchContent.js
+ ```
+
+The docs are versioned so multiple versions can co-exist.
+
+## Build
+
+```sh
+npm run build
+```
+
+## Deployment
+
+See [GitHub Action](../.github/workflows/deploy-v2.yml) for deployment to [preview link](https://microsoft.github.io/playwright).
diff --git a/v2/babel.config.js b/v2/babel.config.js
new file mode 100644
index 0000000000000..e00595dae7d69
--- /dev/null
+++ b/v2/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
+};
diff --git a/v2/blog/2019-05-29-hello-world.md b/v2/blog/2019-05-29-hello-world.md
new file mode 100644
index 0000000000000..8f280066b8e67
--- /dev/null
+++ b/v2/blog/2019-05-29-hello-world.md
@@ -0,0 +1,21 @@
+---
+slug: hello-world
+title: Playwright 1.6
+# author: Endilie Yacop Sucipto
+# author_title: Maintainer of Docusaurus
+# author_url: https://github.com/endiliey
+# author_image_url: https://avatars1.githubusercontent.com/u/17883920?s=460&v=4
+tags: [releases]
+---
+
+Playwright 1.6 is available today, with improvements to network capabilities: inspect WebSocket connections, export network activity as HAR and access resource timing for network responses. This release also adds a new API for touchscreen taps. Like other input methods, touchscreen taps auto-wait for the underlying element to be ready, to improve the reliability of automation.
+
+
+
+Inspect WebSocket connections
+
+Real-time
+
+Network resource timing and HAR exports
+
+Touchscreen tap
diff --git a/v2/docusaurus.config.js b/v2/docusaurus.config.js
new file mode 100644
index 0000000000000..4750b65af6da5
--- /dev/null
+++ b/v2/docusaurus.config.js
@@ -0,0 +1,131 @@
+module.exports = {
+ title: 'Playwright',
+ tagline: 'Fast and reliable end-to-end testing for modern web apps',
+
+ // Repo config for GitHub Pages
+ url: 'https://microsoft.github.io',
+ baseUrl: '/playwright/',
+ organizationName: 'microsoft',
+ projectName: 'playwright',
+
+ onBrokenLinks: 'log',
+ scripts: ['js/redirection.js'],
+ favicon: 'img/playwright-logo.svg',
+ themeConfig: {
+ navbar: {
+ title: 'Playwright',
+ logo: {
+ alt: 'Playwright logo',
+ src: 'img/playwright-logo.svg',
+ },
+ items: [
+ {
+ type: 'doc',
+ docId: 'intro',
+ label: 'Docs',
+ position: 'left',
+ },
+ {
+ type: 'doc',
+ docId: 'api/playwright-module',
+ label: 'API',
+ position: 'left',
+ },
+ {
+ to: 'blog', label: 'Blog', position: 'right'
+ },
+ {
+ href: 'https://github.com/microsoft/playwright',
+ label: 'GitHub',
+ position: 'right',
+ },
+ {
+ type: 'docsVersionDropdown',
+ position: 'left',
+ // Add additional dropdown items at the beginning/end of the dropdown.
+ dropdownItemsBefore: [],
+ dropdownItemsAfter: [{to: '/versions', label: 'All versions'}],
+ // Do not add the link active class when browsing docs.
+ dropdownActiveClassDisabled: true,
+ }
+ ],
+ },
+ footer: {
+ style: 'dark',
+ links: [
+ {
+ title: 'Docs',
+ items: [
+ {
+ label: 'Getting started',
+ to: 'docs/intro',
+ },
+ {
+ label: 'API reference',
+ to: 'docs/api/playwright-module',
+ },
+ ],
+ },
+ {
+ title: 'Community',
+ items: [
+ {
+ label: 'Stack Overflow',
+ href: 'https://stackoverflow.com/questions/tagged/playwright',
+ },
+ {
+ label: 'Slack',
+ href: 'https://aka.ms/playwright-slack',
+ },
+ {
+ label: 'Twitter',
+ href: 'https://twitter.com/playwrightweb',
+ },
+ ],
+ },
+ {
+ title: 'More',
+ items: [
+ {
+ label: 'Blog',
+ to: 'blog',
+ },
+ {
+ label: 'GitHub',
+ href: 'https://github.com/microsoft/playwright',
+ },
+ ],
+ },
+ ],
+ copyright: `Copyright © ${new Date().getFullYear()} Microsoft`,
+ },
+ algolia: {
+ apiKey: 'c85f496c6eea71808027d42111ac550c',
+ indexName: 'playwright',
+ // contextualSearch: true,
+ // searchParameters: {},
+ },
+ },
+ presets: [
+ [
+ '@docusaurus/preset-classic',
+ {
+ docs: {
+ sidebarPath: require.resolve('./sidebars.js'),
+ // Please change this to your repo.
+ editUrl:
+ 'https://github.com/facebook/docusaurus/edit/master/website/'
+ },
+ blog: {
+ showReadingTime: true,
+ // Please change this to your repo.
+ editUrl:
+ 'https://github.com/facebook/docusaurus/edit/master/website/blog/',
+ },
+ theme: {
+ customCss: require.resolve('./src/css/custom.css'),
+ },
+ },
+ ],
+ ],
+};
diff --git a/v2/package.json b/v2/package.json
new file mode 100644
index 0000000000000..4c29ecd009c7b
--- /dev/null
+++ b/v2/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "playwright-dev-v-2",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "docusaurus": "docusaurus",
+ "start": "docusaurus start",
+ "build": "docusaurus build",
+ "swizzle": "docusaurus swizzle",
+ "deploy": "docusaurus deploy",
+ "serve": "docusaurus serve"
+ },
+ "dependencies": {
+ "@docusaurus/core": "2.0.0-alpha.66",
+ "@docusaurus/preset-classic": "2.0.0-alpha.66",
+ "@mdx-js/react": "^1.6.21",
+ "clsx": "^1.1.1",
+ "fs-extra": "^9.0.1",
+ "markdown-it": "^12.0.2",
+ "markdown-it-title": "^3.0.0",
+ "react": "^16.8.4",
+ "react-dom": "^16.8.4",
+ "semver": "^7.3.2",
+ "shelljs": "^0.8.4",
+ "slugify": "^1.4.6"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/v2/scripts/fetchContent.js b/v2/scripts/fetchContent.js
new file mode 100644
index 0000000000000..f298dd3143167
--- /dev/null
+++ b/v2/scripts/fetchContent.js
@@ -0,0 +1,267 @@
+//@ts-check
+const fse = require("fs-extra");
+const path = require("path");
+const md = require("markdown-it")({ typographer: true });
+const slugify = require("slugify");
+const sh = require("shelljs");
+const semver = require("semver");
+
+const { VERSION, SRC_DIR } = process.env;
+if (!SRC_DIR) {
+ console.log("Use SRC_DIR to specify docs location, e.g. path-to-repo/docs.");
+ process.exit(1);
+}
+const srcDir = SRC_DIR;
+const destDir = (function () {
+ return VERSION ? path.join("versioned_docs", `version-${VERSION}`) : "./docs";
+})();
+const sidebarFile = (function () {
+ return VERSION
+ ? path.join("versioned_sidebars", `version-${VERSION}-sidebars.json`)
+ : "sidebars.js";
+})();
+const versionsFile = "versions.json";
+
+function slugger(text) {
+ //@ts-ignore
+ return slugify(text, { lower: true, strict: true });
+}
+
+function getTitle(contents) {
+ const env = {};
+ md.use(require("markdown-it-title"), 0).render(contents, env);
+ return env.title;
+}
+
+function removeHeadingLine(contents) {
+ const lines = contents.split("\n");
+ const findIndex = lines.findIndex((l) => l.startsWith("# "));
+ return lines.slice(findIndex + 1).join("\n");
+}
+
+function writeFrontmatter(filePath) {
+ const contents = fse.readFileSync(filePath).toString();
+ const title = getTitle(contents);
+ const fileName = path.basename(filePath, path.extname(filePath));
+ const fm = `---\nid: ${fileName}\ntitle: "${title}"\n---\n\n`;
+ const newContents = removeHeadingLine(contents);
+ fse.writeFileSync(filePath, `${fm}${newContents}`);
+}
+
+function closeTags(filePath) {
+ // replaces with and
with
+ // + special handling for troubleshooting.md
+ const contents = fse.readFileSync(filePath).toString();
+ const newContents = contents
+ .replace(/(])*)>/gi, "$1/>")
+ .replace(/(
])*)>/gi, "$1/>")
+ .replace("
\n", "
\n\n");
+ fse.writeFileSync(filePath, newContents);
+}
+
+function markdownFiles(dir) {
+ let files = [];
+ fse.readdirSync(dir).forEach((file) => {
+ let fullPath = path.join(dir, file);
+ if (fse.lstatSync(fullPath).isDirectory()) {
+ files.push(...markdownFiles(fullPath));
+ } else {
+ files.push(fullPath);
+ }
+ });
+ return files.filter((name) => name.endsWith(".md"));
+}
+
+function keepOnlyMarkdownFiles(dir) {
+ fse.readdirSync(dir).forEach((file) => {
+ let fullPath = path.join(dir, file);
+ if (fse.lstatSync(fullPath).isDirectory()) {
+ keepOnlyMarkdownFiles(fullPath);
+ } else {
+ if (!fullPath.endsWith(".md")) {
+ fse.removeSync(fullPath);
+ }
+ }
+ });
+}
+
+function splitApi(contents) {
+ const tokens = md.parse(contents, {});
+ const headings = tokens.filter(
+ (t) => t.type === "heading_open" && t.tag === "h3"
+ );
+ const lineNums = headings.map((h) => h.map[0]);
+ const pairs = lineNums.reduce(function (result, value, index, array) {
+ if (index < array.length - 1) {
+ result.push(array.slice(index, index + 2));
+ } else {
+ result.push([value, contents.split("\n").length]);
+ }
+ return result;
+ }, []);
+ const lines = contents.split("\n");
+ // TODO: copy footnote links to all pages
+ // TODO: also fix the links since we are splitting api.md
+
+ pairs.forEach((p) => {
+ fse.mkdirpSync(path.join(destDir, "api"));
+ const contents = lines.slice(p[0], p[1]).join("\n");
+ const title = getTitle(contents);
+ const slug = slugger(title);
+ const filePath = path.join(destDir, "api", `${slug}.md`);
+ // promote headings by 2 levels
+ const newContents = contents
+ .replace(/###/g, "#")
+ .replace(/####/g, "##")
+ .replace(/#####/g, "###");
+ fse.writeFileSync(filePath, newContents);
+ });
+}
+
+function generateApiSidebar(contents) {
+ // parse the table of contents
+ const tokens = md.parse(contents, {});
+ const ul = tokens.find((t) => t.type === "bullet_list_open");
+ const listItems = tokens.filter(
+ (t) => t.type === "inline" && t.map[0] >= ul.map[0] && t.map[1] <= ul.map[1]
+ );
+ const ids = listItems
+ .map((li) => li.children.find((c) => c.type === "text"))
+ .map((t) => t.content)
+ .map(slugger);
+ const prefix = VERSION ? `version-${VERSION}/api/` : "api/";
+ return [
+ {
+ type: "category",
+ label: "API reference",
+ items: ids.map((id) => ({ type: "doc", id: `${prefix}${id}` })),
+ collapsed: false,
+ },
+ ];
+}
+
+function generateDocsSidebar(contents) {
+ // For pre-v1.2.0 tags, the sidebar does not have categories
+ // and is a flat list of links to docs.
+ const tokens = md.parse(contents, {});
+ const ol = tokens.find((t) => t.type === "ordered_list_open");
+ const headings = tokens.filter(
+ (t) =>
+ t.type === "inline" &&
+ t.map[0] >= ol.map[0] &&
+ t.map[1] <= ol.map[1] &&
+ t.level === 3
+ );
+ const prefix = VERSION ? `version-${VERSION}/` : "";
+
+ function headingContent(token) {
+ return token.children.find((t) => t.type === "text").content;
+ }
+
+ function headingLink(token) {
+ const link = token.children.find((t) => t.type === "link_open");
+ const id = link.attrs[0][1].replace("./", "").replace(".md", "");
+ return `${prefix}${id}`;
+ }
+
+ function subList(headingToken) {
+ const ul = tokens.find(
+ (t) => t.type === "bullet_list_open" && t.map[0] >= headingToken.map[0]
+ );
+ if (!ul) {
+ // Ignore. This should be only the "API reference" heading in pre-v1.2.0 tags
+ return undefined;
+ }
+ const items = tokens.filter(
+ (t) =>
+ t.type === "inline" && t.map[0] >= ul.map[0] && t.map[1] <= ul.map[1]
+ );
+ const hrefs = items.map((i) => i.children[0].attrs[0][1]);
+ return {
+ type: "category",
+ label: headingContent(headingToken),
+ items: hrefs
+ .filter((h) => h.endsWith(".md"))
+ .map((h) => h.replace("./", "").replace(".md", ""))
+ .filter((h) => h !== "api")
+ .map((id) => ({ type: "doc", id: `${prefix}${id}` })),
+ collapsed: false,
+ };
+ }
+
+ const hasCategories = !VERSION || !semver.lt(VERSION, "1.3.0");
+ return hasCategories
+ ? headings.map(subList).filter(Boolean)
+ : headings.map((h) => ({
+ type: "doc",
+ // label: headingContent(h),
+ id: headingLink(h),
+ }));
+}
+
+function writeSidebarFile(apiSidebar, docsSidebar) {
+ const sidebar = {};
+ const docsKey = VERSION ? `version-${VERSION}/docs` : "docs";
+ const apiKey = VERSION ? `version-${VERSION}/api` : "api";
+ sidebar[docsKey] = docsSidebar;
+ sidebar[apiKey] = apiSidebar;
+ const content = VERSION
+ ? JSON.stringify(sidebar)
+ : `module.exports = ${JSON.stringify(sidebar)};`;
+ fse.ensureFileSync(sidebarFile);
+ fse.writeFileSync(sidebarFile, content);
+}
+
+function copyFiles() {
+ const currentDir = sh.pwd().stdout;
+ sh.cd(srcDir);
+ const tag = VERSION ? `tags/v${VERSION}` : `master`;
+ const result = sh.exec(`git checkout ${tag}`);
+ if (result.code !== 0) {
+ console.warn(`git checkout to ${tag} failed. Check source repo.`);
+ process.exit(1);
+ }
+ sh.cd(currentDir);
+ fse.copySync(srcDir, destDir);
+}
+
+function writeVersionsFile() {
+ let newVersions = [];
+ if (fse.existsSync(versionsFile)) {
+ const versions = JSON.parse(fse.readFileSync("versions.json").toString());
+ versions.unshift(VERSION);
+ newVersions = versions;
+ } else {
+ newVersions = [VERSION];
+ }
+ const uniqVersions = semver.rsort([...new Set(newVersions)]);
+ fse.writeFileSync("versions.json", JSON.stringify(uniqVersions));
+}
+
+// Main
+fse.mkdirpSync(destDir);
+fse.emptyDirSync(destDir);
+copyFiles();
+keepOnlyMarkdownFiles(destDir);
+
+// Transform API reference
+const api = fse.readFileSync(path.join(destDir, "api.md")).toString();
+splitApi(api);
+const apiSidebar = generateApiSidebar(api);
+
+// Transform markdown files
+const files = markdownFiles(destDir);
+files.forEach((filePath) => {
+ writeFrontmatter(filePath);
+ closeTags(filePath);
+});
+const docsSidebar = generateDocsSidebar(
+ fse.readFileSync(path.join(destDir, "README.md")).toString()
+);
+
+// Create sidebar
+writeSidebarFile(apiSidebar, docsSidebar);
+
+if (VERSION) {
+ writeVersionsFile();
+}
diff --git a/v2/src/css/custom.css b/v2/src/css/custom.css
new file mode 100644
index 0000000000000..74ba0f27f39d5
--- /dev/null
+++ b/v2/src/css/custom.css
@@ -0,0 +1,25 @@
+/* stylelint-disable docusaurus/copyright-header */
+/**
+ * Any CSS included here will be global. The classic template
+ * bundles Infima by default. Infima is a CSS framework designed to
+ * work well for content-centric websites.
+ */
+
+/* You can override the default Infima variables here. */
+:root {
+ --ifm-color-primary: #25c2a0;
+ --ifm-color-primary-dark: rgb(33, 175, 144);
+ --ifm-color-primary-darker: rgb(31, 165, 136);
+ --ifm-color-primary-darkest: rgb(26, 136, 112);
+ --ifm-color-primary-light: rgb(70, 203, 174);
+ --ifm-color-primary-lighter: rgb(102, 212, 189);
+ --ifm-color-primary-lightest: rgb(146, 224, 208);
+ --ifm-code-font-size: 95%;
+}
+
+.docusaurus-highlight-code-line {
+ background-color: rgb(72, 77, 91);
+ display: block;
+ margin: 0 calc(-1 * var(--ifm-pre-padding));
+ padding: 0 var(--ifm-pre-padding);
+}
diff --git a/v2/src/pages/index.js b/v2/src/pages/index.js
new file mode 100644
index 0000000000000..4904027ffb6d4
--- /dev/null
+++ b/v2/src/pages/index.js
@@ -0,0 +1,94 @@
+import React from 'react';
+import clsx from 'clsx';
+import Layout from '@theme/Layout';
+import Link from '@docusaurus/Link';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+import styles from './styles.module.css';
+
+const features = [
+ {
+ title: 'Test across all modern browsers',
+ imageUrl: 'img/undraw_docusaurus_mountain.svg',
+ description: (
+ <>
+ Single API to author browser tests for Chromium, Firefox and WebKit.
+ >
+ ),
+ },
+ {
+ title: 'Use in your preferred language',
+ imageUrl: 'img/undraw_docusaurus_tree.svg',
+ description: (
+ <>
+ Use the API in JavaScript/TypeScript, Python, C# and Java.
+ >
+ ),
+ },
+ {
+ title: 'Automation without trade-offs',
+ imageUrl: 'img/undraw_docusaurus_react.svg',
+ description: (
+ <>
+ Playwright is built to automate the broad and growing set of web browser capabilities.
+ >
+ ),
+ },
+];
+
+function Feature({imageUrl, title, description}) {
+ const imgUrl = useBaseUrl(imageUrl);
+ return (
+
{description}
+{siteConfig.tagline}
+{stableVersion.name} | ++ Documentation + | ++ + Release Notes + + | +
---|
master | ++ Documentation + | ++ Source Code + | +
---|
{version.label} | ++ Documentation + | ++ + Release Notes + + | +
---|