@wp-playground/blueprints 0.1.28 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { PHPResponse, UniversalPHP } from '@php-wasm/universal';
2
2
  import { phpVars } from '@php-wasm/util';
3
- import { BaseStep } from '.';
3
+ import { StepHandler } from '.';
4
4
 
5
5
  // @ts-ignore
6
6
  import migrationsPHPCode from './migration.php?raw';
@@ -9,17 +9,6 @@ import migrationsPHPCode from './migration.php?raw';
9
9
  * Full site export support:
10
10
  */
11
11
 
12
- export interface ReplaceSiteStep<ResourceType> extends BaseStep {
13
- step: 'replaceSite';
14
- fullSiteZip: ResourceType;
15
- }
16
-
17
- export interface UnzipStep extends BaseStep {
18
- step: 'unzip';
19
- zipPath: string;
20
- extractToPath: string;
21
- }
22
-
23
12
  /**
24
13
  * Export the current site as a zip file.
25
14
  *
@@ -44,13 +33,21 @@ export async function zipEntireSite(playground: UniversalPHP) {
44
33
  return new File([fileBuffer], zipName);
45
34
  }
46
35
 
36
+ export interface ReplaceSiteStep<ResourceType> {
37
+ step: 'replaceSite';
38
+ fullSiteZip: ResourceType;
39
+ }
40
+
47
41
  /**
48
42
  * Replace the current site with the contents of a full site zip file.
49
43
  *
50
44
  * @param playground Playground client.
51
45
  * @param fullSiteZip Zipped WordPress site.
52
46
  */
53
- export async function replaceSite(playground: UniversalPHP, fullSiteZip: File) {
47
+ export const replaceSite: StepHandler<ReplaceSiteStep<File>> = async (
48
+ playground,
49
+ { fullSiteZip }
50
+ ) => {
54
51
  const zipPath = '/import.zip';
55
52
  await playground.writeFile(
56
53
  zipPath,
@@ -61,7 +58,7 @@ export async function replaceSite(playground: UniversalPHP, fullSiteZip: File) {
61
58
  const documentRoot = await playground.documentRoot;
62
59
 
63
60
  await playground.rmdir(documentRoot);
64
- await unzip(playground, zipPath, '/');
61
+ await unzip(playground, { zipPath, extractToPath: '/' });
65
62
 
66
63
  const js = phpVars({ absoluteUrl });
67
64
  await updateFile(
@@ -75,6 +72,12 @@ export async function replaceSite(playground: UniversalPHP, fullSiteZip: File) {
75
72
  }
76
73
  ?>${contents}`
77
74
  );
75
+ };
76
+
77
+ export interface UnzipStep {
78
+ step: 'unzip';
79
+ zipPath: string;
80
+ extractToPath: string;
78
81
  }
79
82
 
80
83
  /**
@@ -84,17 +87,19 @@ export async function replaceSite(playground: UniversalPHP, fullSiteZip: File) {
84
87
  * @param zipPath The zip file to unzip.
85
88
  * @param extractTo The directory to extract the zip file to.
86
89
  */
87
- export async function unzip(
88
- playground: UniversalPHP,
89
- zipPath: string,
90
- extractTo: string
91
- ) {
90
+ export const unzip: StepHandler<UnzipStep> = async (
91
+ playground,
92
+ { zipPath, extractToPath }
93
+ ) => {
92
94
  const js = phpVars({
93
95
  zipPath,
94
- extractTo,
96
+ extractToPath,
95
97
  });
96
- await phpMigration(playground, `unzip(${js.zipPath}, ${js.extractTo});`);
97
- }
98
+ await phpMigration(
99
+ playground,
100
+ `unzip(${js.zipPath}, ${js.extractToPath});`
101
+ );
102
+ };
98
103
 
99
104
  /**
100
105
  * WXR and WXZ files support:
@@ -128,7 +133,7 @@ export async function exportWXZ(playground: UniversalPHP) {
128
133
  return new File([databaseExportResponse.bytes], 'export.wxz');
129
134
  }
130
135
 
131
- export interface ImportFileStep<ResourceType> extends BaseStep {
136
+ export interface ImportFileStep<ResourceType> {
132
137
  step: 'importFile';
133
138
  file: ResourceType;
134
139
  }
@@ -141,7 +146,10 @@ export interface ImportFileStep<ResourceType> extends BaseStep {
141
146
  * @param playground Playground client.
142
147
  * @param file The file to import.
143
148
  */
144
- export async function submitImporterForm(playground: UniversalPHP, file: File) {
149
+ export const importFile: StepHandler<ImportFileStep<File>> = async (
150
+ playground,
151
+ { file }
152
+ ) => {
145
153
  const importerPageOneResponse = await playground.request({
146
154
  url: '/wp-admin/admin.php?import=wordpress',
147
155
  });
@@ -177,12 +185,12 @@ export async function submitImporterForm(playground: UniversalPHP, file: File) {
177
185
  }
178
186
  }
179
187
 
180
- return await playground.request({
188
+ await playground.request({
181
189
  url: importForm.action,
182
190
  method: 'POST',
183
191
  formData: data,
184
192
  });
185
- }
193
+ };
186
194
 
187
195
  function DOM(response: PHPResponse) {
188
196
  return new DOMParser().parseFromString(response.text, 'text/html');
@@ -1,38 +1,8 @@
1
- export {
2
- zipEntireSite,
3
- exportWXR,
4
- exportWXZ,
5
- replaceSite,
6
- submitImporterForm,
7
- } from './import-export';
8
- export { login } from './login';
9
- export { installTheme } from './install-theme';
10
- export type { InstallThemeOptions } from './install-theme';
11
- export { installPlugin } from './install-plugin';
12
- export type { InstallPluginOptions } from './install-plugin';
13
- export { activatePlugin } from './activate-plugin';
14
- export { setSiteOptions, updateUserMeta } from './site-data';
15
- export type { SiteOptions, UserMeta } from './site-data';
16
- export { defineSiteUrl } from './define-site-url';
17
- export { applyWordPressPatches } from './apply-wordpress-patches';
18
- export type { PatchOptions } from './apply-wordpress-patches';
19
- export { runWpInstallationWizard } from './run-wp-installation-wizard';
20
- export type { WordPressInstallationOptions } from './run-wp-installation-wizard';
21
-
1
+ import { ProgressTracker } from '@php-wasm/progress';
2
+ import { UniversalPHP } from '@php-wasm/universal';
3
+ import { FileReference } from '../resources';
22
4
  import { ActivatePluginStep } from './activate-plugin';
23
5
  import { ApplyWordPressPatchesStep } from './apply-wordpress-patches';
24
- import {
25
- RunPHPStep,
26
- RunPHPWithOptionsStep,
27
- SetPhpIniEntryStep,
28
- RequestStep,
29
- CpStep,
30
- RmStep,
31
- RmdirStep,
32
- MvStep,
33
- MkdirStep,
34
- WriteFileStep,
35
- } from './client-methods';
36
6
  import { DefineSiteUrlStep } from './define-site-url';
37
7
  import { ImportFileStep, ReplaceSiteStep, UnzipStep } from './import-export';
38
8
  import { InstallPluginStep } from './install-plugin';
@@ -40,59 +10,56 @@ import { InstallThemeStep } from './install-theme';
40
10
  import { LoginStep } from './login';
41
11
  import { RunWpInstallationWizardStep } from './run-wp-installation-wizard';
42
12
  import { SetSiteOptionsStep, UpdateUserMetaStep } from './site-data';
43
- export interface BaseStep {
44
- step: string;
13
+ import {
14
+ RmStep,
15
+ CpStep,
16
+ MkdirStep,
17
+ RmdirStep,
18
+ MvStep,
19
+ SetPhpIniEntryStep,
20
+ RunPHPStep,
21
+ RunPHPWithOptionsStep,
22
+ RequestStep,
23
+ WriteFileStep,
24
+ } from './client-methods';
25
+
26
+ export type Step = GenericStep<FileReference>;
27
+ export type StepDefinition = Step & {
45
28
  progress?: {
46
29
  weight?: number;
47
30
  caption?: string;
48
31
  };
49
- }
32
+ };
50
33
 
51
- export type Step<ResourceType> =
52
- | InstallPluginStep<ResourceType>
53
- | InstallThemeStep<ResourceType>
54
- | LoginStep
55
- | ImportFileStep<ResourceType>
34
+ export type GenericStep<Resource> =
56
35
  | ActivatePluginStep
57
- | ReplaceSiteStep<ResourceType>
58
- | UnzipStep
59
- | DefineSiteUrlStep
60
36
  | ApplyWordPressPatchesStep
61
- | RunWpInstallationWizardStep
62
- | SetSiteOptionsStep
63
- | UpdateUserMetaStep
64
- | RunPHPStep
65
- | RunPHPWithOptionsStep
66
- | SetPhpIniEntryStep
67
- | RequestStep
68
37
  | CpStep
38
+ | DefineSiteUrlStep
39
+ | ImportFileStep<Resource>
40
+ | InstallPluginStep<Resource>
41
+ | InstallThemeStep<Resource>
42
+ | LoginStep
43
+ | MkdirStep
44
+ | MvStep
45
+ | RequestStep
46
+ | ReplaceSiteStep<Resource>
69
47
  | RmStep
70
48
  | RmdirStep
71
- | MvStep
72
- | MkdirStep
73
- | WriteFileStep<ResourceType>;
49
+ | RunPHPStep
50
+ | RunPHPWithOptionsStep
51
+ | RunWpInstallationWizardStep
52
+ | SetPhpIniEntryStep
53
+ | SetSiteOptionsStep
54
+ | UnzipStep
55
+ | UpdateUserMetaStep
56
+ | WriteFileStep<Resource>;
74
57
 
75
- export type {
76
- InstallPluginStep,
77
- InstallThemeStep,
78
- LoginStep,
79
- ImportFileStep,
80
- ActivatePluginStep,
81
- ReplaceSiteStep,
82
- UnzipStep,
83
- DefineSiteUrlStep,
84
- ApplyWordPressPatchesStep,
85
- RunWpInstallationWizardStep,
86
- SetSiteOptionsStep,
87
- UpdateUserMetaStep,
88
- RunPHPStep,
89
- RunPHPWithOptionsStep,
90
- SetPhpIniEntryStep,
91
- RequestStep,
92
- CpStep,
93
- RmStep,
94
- RmdirStep,
95
- MvStep,
96
- MkdirStep,
97
- WriteFileStep,
98
- };
58
+ export type StepHandler<S extends GenericStep<File>> = (
59
+ php: UniversalPHP,
60
+ args: Omit<S, 'step'>,
61
+ progressArgs?: {
62
+ tracker: ProgressTracker;
63
+ initialCaption?: string;
64
+ }
65
+ ) => any;
@@ -1,8 +1,8 @@
1
1
  import { UniversalPHP } from '@php-wasm/universal';
2
- import { BaseStep } from '.';
3
- import { asDOM } from './common';
2
+ import { StepHandler } from '.';
3
+ import { asDOM, zipNameToHumanName } from './common';
4
4
 
5
- export interface InstallPluginStep<ResourceType> extends BaseStep {
5
+ export interface InstallPluginStep<ResourceType> {
6
6
  step: 'installPlugin';
7
7
  pluginZipFile: ResourceType;
8
8
  options?: InstallPluginOptions;
@@ -24,105 +24,122 @@ export interface InstallPluginOptions {
24
24
  * @param pluginZipFile The plugin zip file.
25
25
  * @param options Optional. Set `activate` to false if you don't want to activate the plugin.
26
26
  */
27
- export async function installPlugin(
28
- playground: UniversalPHP,
29
- pluginZipFile: File,
30
- options: InstallPluginOptions = {}
31
- ) {
32
- const activate = 'activate' in options ? options.activate : true;
33
-
34
- // Upload it to WordPress
35
- const pluginForm = await playground.request({
36
- url: '/wp-admin/plugin-install.php?tab=upload',
37
- });
38
- const pluginFormPage = asDOM(pluginForm);
39
- const pluginFormData = new FormData(
40
- pluginFormPage.querySelector('.wp-upload-form')! as HTMLFormElement
41
- ) as any;
42
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
43
- const { pluginzip, ...postData } = Object.fromEntries(
44
- pluginFormData.entries()
27
+ export const installPlugin: StepHandler<InstallPluginStep<File>> = async (
28
+ playground,
29
+ { pluginZipFile, options = {} },
30
+ progress?
31
+ ) => {
32
+ progress?.tracker.setCaption(
33
+ `Installing the ${zipNameToHumanName(pluginZipFile?.name)} plugin`
45
34
  );
35
+ try {
36
+ const activate = 'activate' in options ? options.activate : true;
46
37
 
47
- const pluginInstalledResponse = await playground.request({
48
- url: '/wp-admin/update.php?action=upload-plugin',
49
- method: 'POST',
50
- formData: postData,
51
- files: { pluginzip: pluginZipFile },
52
- });
38
+ // Upload it to WordPress
39
+ const pluginForm = await playground.request({
40
+ url: '/wp-admin/plugin-install.php?tab=upload',
41
+ });
42
+ const pluginFormPage = asDOM(pluginForm);
43
+ const pluginFormData = new FormData(
44
+ pluginFormPage.querySelector('.wp-upload-form')! as HTMLFormElement
45
+ ) as any;
46
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
47
+ const { pluginzip, ...postData } = Object.fromEntries(
48
+ pluginFormData.entries()
49
+ );
53
50
 
54
- // Activate if needed
55
- if (activate) {
56
- const pluginInstalledPage = asDOM(pluginInstalledResponse);
57
- const activateButtonHref = pluginInstalledPage
58
- .querySelector('#wpbody-content .button.button-primary')!
59
- .attributes.getNamedItem('href')!.value;
60
- const activatePluginUrl = new URL(
61
- activateButtonHref,
62
- await playground.pathToInternalUrl('/wp-admin/')
63
- ).toString();
64
- await playground.request({
65
- url: activatePluginUrl,
51
+ const pluginInstalledResponse = await playground.request({
52
+ url: '/wp-admin/update.php?action=upload-plugin',
53
+ method: 'POST',
54
+ formData: postData,
55
+ files: { pluginzip: pluginZipFile },
66
56
  });
67
- }
68
57
 
69
- /**
70
- * Pair the site editor's nested iframe to the Service Worker.
71
- *
72
- * Without the patch below, the site editor initiates network requests that
73
- * aren't routed through the service worker. That's a known browser issue:
74
- *
75
- * * https://bugs.chromium.org/p/chromium/issues/detail?id=880768
76
- * * https://bugzilla.mozilla.org/show_bug.cgi?id=1293277
77
- * * https://github.com/w3c/ServiceWorker/issues/765
78
- *
79
- * The problem with iframes using srcDoc and src="about:blank" as they
80
- * fail to inherit the root site's service worker.
81
- *
82
- * Gutenberg loads the site editor using <iframe srcDoc="<!doctype html">
83
- * to force the standards mode and not the quirks mode:
84
- *
85
- * https://github.com/WordPress/gutenberg/pull/38855
86
- *
87
- * This commit patches the site editor to achieve the same result via
88
- * <iframe src="/doctype.html"> and a doctype.html file containing just
89
- * `<!doctype html>`. This allows the iframe to inherit the service worker
90
- * and correctly load all the css, js, fonts, images, and other assets.
91
- *
92
- * Ideally this issue would be fixed directly in Gutenberg and the patch
93
- * below would be removed.
94
- *
95
- * See https://github.com/WordPress/wordpress-playground/issues/42 for more details
96
- */
97
- async function updateFile(
98
- path: string,
99
- callback: (contents: string) => string
100
- ) {
101
- return await playground.writeFile(
102
- path,
103
- callback(await playground.readFileAsText(path))
104
- );
105
- }
106
- if (
107
- (await playground.isDir('/wordpress/wp-content/plugins/gutenberg')) &&
108
- !(await playground.fileExists('/wordpress/.gutenberg-patched'))
109
- ) {
110
- await playground.writeFile('/wordpress/.gutenberg-patched', '1');
111
- await updateFile(
112
- `/wordpress/wp-content/plugins/gutenberg/build/block-editor/index.js`,
113
- (contents) =>
114
- contents.replace(
115
- /srcDoc:("[^"]+"|[^,]+)/g,
116
- 'src:"/wp-includes/empty.html"'
117
- )
118
- );
119
- await updateFile(
120
- `/wordpress/wp-content/plugins/gutenberg/build/block-editor/index.min.js`,
121
- (contents) =>
122
- contents.replace(
123
- /srcDoc:("[^"]+"|[^,]+)/g,
124
- 'src:"/wp-includes/empty.html"'
125
- )
58
+ // Activate if needed
59
+ if (activate) {
60
+ const pluginInstalledPage = asDOM(pluginInstalledResponse);
61
+ const activateButtonHref = pluginInstalledPage
62
+ .querySelector('#wpbody-content .button.button-primary')!
63
+ .attributes.getNamedItem('href')!.value;
64
+ const activatePluginUrl = new URL(
65
+ activateButtonHref,
66
+ await playground.pathToInternalUrl('/wp-admin/')
67
+ ).toString();
68
+ await playground.request({
69
+ url: activatePluginUrl,
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Pair the site editor's nested iframe to the Service Worker.
75
+ *
76
+ * Without the patch below, the site editor initiates network requests that
77
+ * aren't routed through the service worker. That's a known browser issue:
78
+ *
79
+ * * https://bugs.chromium.org/p/chromium/issues/detail?id=880768
80
+ * * https://bugzilla.mozilla.org/show_bug.cgi?id=1293277
81
+ * * https://github.com/w3c/ServiceWorker/issues/765
82
+ *
83
+ * The problem with iframes using srcDoc and src="about:blank" as they
84
+ * fail to inherit the root site's service worker.
85
+ *
86
+ * Gutenberg loads the site editor using <iframe srcDoc="<!doctype html">
87
+ * to force the standards mode and not the quirks mode:
88
+ *
89
+ * https://github.com/WordPress/gutenberg/pull/38855
90
+ *
91
+ * This commit patches the site editor to achieve the same result via
92
+ * <iframe src="/doctype.html"> and a doctype.html file containing just
93
+ * `<!doctype html>`. This allows the iframe to inherit the service worker
94
+ * and correctly load all the css, js, fonts, images, and other assets.
95
+ *
96
+ * Ideally this issue would be fixed directly in Gutenberg and the patch
97
+ * below would be removed.
98
+ *
99
+ * See https://github.com/WordPress/wordpress-playground/issues/42 for more details
100
+ */
101
+ if (
102
+ (await playground.isDir(
103
+ '/wordpress/wp-content/plugins/gutenberg'
104
+ )) &&
105
+ !(await playground.fileExists('/wordpress/.gutenberg-patched'))
106
+ ) {
107
+ await playground.writeFile('/wordpress/.gutenberg-patched', '1');
108
+ await updateFile(
109
+ playground,
110
+ `/wordpress/wp-content/plugins/gutenberg/build/block-editor/index.js`,
111
+ (contents) =>
112
+ contents.replace(
113
+ /srcDoc:("[^"]+"|[^,]+)/g,
114
+ 'src:"/wp-includes/empty.html"'
115
+ )
116
+ );
117
+ await updateFile(
118
+ playground,
119
+ `/wordpress/wp-content/plugins/gutenberg/build/block-editor/index.min.js`,
120
+ (contents) =>
121
+ contents.replace(
122
+ /srcDoc:("[^"]+"|[^,]+)/g,
123
+ 'src:"/wp-includes/empty.html"'
124
+ )
125
+ );
126
+ }
127
+ } catch (error) {
128
+ console.error(
129
+ `Proceeding without the ${pluginZipFile.name} theme. Could not install it in wp-admin. ` +
130
+ `The original error was: ${error}`
126
131
  );
132
+ console.error(error);
127
133
  }
134
+ };
135
+
136
+ async function updateFile(
137
+ playground: UniversalPHP,
138
+ path: string,
139
+ callback: (contents: string) => string
140
+ ) {
141
+ return await playground.writeFile(
142
+ path,
143
+ callback(await playground.readFileAsText(path))
144
+ );
128
145
  }
@@ -1,8 +1,7 @@
1
- import { UniversalPHP } from '@php-wasm/universal';
2
- import { BaseStep } from '.';
3
- import { asDOM } from './common';
1
+ import { StepHandler } from '.';
2
+ import { asDOM, zipNameToHumanName } from './common';
4
3
 
5
- export interface InstallThemeStep<ResourceType> extends BaseStep {
4
+ export interface InstallThemeStep<ResourceType> {
6
5
  step: 'installTheme';
7
6
  themeZipFile: ResourceType;
8
7
  options?: InstallThemeOptions;
@@ -24,66 +23,77 @@ export interface InstallThemeOptions {
24
23
  * @param themeZipFile The theme zip file.
25
24
  * @param options Optional. Set `activate` to false if you don't want to activate the theme.
26
25
  */
27
- export async function installTheme(
28
- playground: UniversalPHP,
29
- themeZipFile: File,
30
- options: InstallThemeOptions = {}
31
- ) {
32
- const activate = 'activate' in options ? options.activate : true;
33
-
34
- // Upload it to WordPress
35
- const themeForm = await playground.request({
36
- url: '/wp-admin/theme-install.php',
37
- });
38
- const themeFormPage = asDOM(themeForm);
39
- const themeFormData = new FormData(
40
- themeFormPage.querySelector('.wp-upload-form')! as HTMLFormElement
41
- ) as any;
42
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
43
- const { themezip, ...postData } = Object.fromEntries(
44
- themeFormData.entries()
26
+ export const installTheme: StepHandler<InstallThemeStep<File>> = async (
27
+ playground,
28
+ { themeZipFile, options = {} },
29
+ progress
30
+ ) => {
31
+ progress?.tracker.setCaption(
32
+ `Installing the ${zipNameToHumanName(themeZipFile.name)} theme`
45
33
  );
34
+ try {
35
+ const activate = 'activate' in options ? options.activate : true;
46
36
 
47
- const themeInstalledResponse = await playground.request({
48
- url: '/wp-admin/update.php?action=upload-theme',
49
- method: 'POST',
50
- formData: postData,
51
- files: { themezip: themeZipFile },
52
- });
37
+ // Upload it to WordPress
38
+ const themeForm = await playground.request({
39
+ url: '/wp-admin/theme-install.php',
40
+ });
41
+ const themeFormPage = asDOM(themeForm);
42
+ const themeFormData = new FormData(
43
+ themeFormPage.querySelector('.wp-upload-form')! as HTMLFormElement
44
+ ) as any;
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ const { themezip, ...postData } = Object.fromEntries(
47
+ themeFormData.entries()
48
+ );
53
49
 
54
- // Activate if needed
55
- if (activate) {
56
- const themeInstalledPage = asDOM(themeInstalledResponse);
50
+ const themeInstalledResponse = await playground.request({
51
+ url: '/wp-admin/update.php?action=upload-theme',
52
+ method: 'POST',
53
+ formData: postData,
54
+ files: { themezip: themeZipFile },
55
+ });
57
56
 
58
- const messageContainer = themeInstalledPage.querySelector(
59
- '#wpbody-content > .wrap'
60
- );
61
- if (
62
- messageContainer?.textContent?.includes(
63
- 'Theme installation failed.'
64
- )
65
- ) {
66
- console.error(messageContainer?.textContent);
67
- return;
68
- }
57
+ // Activate if needed
58
+ if (activate) {
59
+ const themeInstalledPage = asDOM(themeInstalledResponse);
69
60
 
70
- const activateButton = themeInstalledPage.querySelector(
71
- '#wpbody-content .activatelink, ' +
72
- '.update-from-upload-actions .button.button-primary'
73
- );
74
- if (!activateButton) {
75
- console.error('The "activate" button was not found.');
76
- return;
77
- }
61
+ const messageContainer = themeInstalledPage.querySelector(
62
+ '#wpbody-content > .wrap'
63
+ );
64
+ if (
65
+ messageContainer?.textContent?.includes(
66
+ 'Theme installation failed.'
67
+ )
68
+ ) {
69
+ console.error(messageContainer?.textContent);
70
+ return;
71
+ }
78
72
 
79
- const activateButtonHref =
80
- activateButton.attributes.getNamedItem('href')!.value;
81
- const activateThemeUrl = new URL(
82
- activateButtonHref,
83
- await playground.pathToInternalUrl('/wp-admin/')
84
- ).toString();
85
- await playground.request({
86
- url: activateThemeUrl,
87
- });
73
+ const activateButton = themeInstalledPage.querySelector(
74
+ '#wpbody-content .activatelink, ' +
75
+ '.update-from-upload-actions .button.button-primary'
76
+ );
77
+ if (!activateButton) {
78
+ console.error('The "activate" button was not found.');
79
+ return;
80
+ }
81
+
82
+ const activateButtonHref =
83
+ activateButton.attributes.getNamedItem('href')!.value;
84
+ const activateThemeUrl = new URL(
85
+ activateButtonHref,
86
+ await playground.pathToInternalUrl('/wp-admin/')
87
+ ).toString();
88
+ await playground.request({
89
+ url: activateThemeUrl,
90
+ });
91
+ }
92
+ } catch (error) {
93
+ console.error(
94
+ `Proceeding without the ${themeZipFile.name} theme. Could not install it in wp-admin. ` +
95
+ `The original error was: ${error}`
96
+ );
97
+ console.error(error);
88
98
  }
89
- }
99
+ };