wp-advads 1.0.31 → 1.0.34
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.
- package/.github/workflows/test.yml +17 -0
- package/CHANGELOG.md +23 -1
- package/LICENSE +15 -0
- package/README.md +8 -7
- package/package.json +9 -7
- package/src/app.js +17 -11
- package/src/commands/build.js +72 -59
- package/src/commands/class.js +34 -40
- package/src/commands/css.js +13 -19
- package/src/commands/init.js +4 -1
- package/src/commands/javascript.js +13 -19
- package/src/commands/release/change-version.js +14 -17
- package/src/commands/release/changelog.js +23 -31
- package/src/commands/release/git.js +25 -13
- package/src/commands/release/github.js +63 -12
- package/src/commands/release/semver.js +22 -25
- package/src/commands/release/translations.js +17 -23
- package/src/commands/release.js +3 -4
- package/src/commands/updates.js +20 -17
- package/src/commands/views.js +16 -18
- package/src/commands/wp-glotpress.js +125 -126
- package/src/commands/wp-pot.js +17 -9
- package/src/utilities/filesystem.js +57 -36
- package/src/utilities/flow.js +21 -0
- package/src/utilities/formatting.js +13 -4
- package/src/utilities/index.js +1 -1
- package/src/utilities/misc.js +1 -6
- package/src/utilities/settings.js +34 -14
- package/src/utilities/shell.js +60 -8
- package/template/files/class-singleton.php +5 -0
- package/template/files/javascript.js +0 -2
- package/template/files/view.php +2 -0
- package/test/build.test.js +168 -0
- package/test/changelog-locales.test.js +49 -0
- package/test/class.test.js +47 -0
- package/test/compile-templates.test.js +70 -0
- package/test/deps-hygiene.test.js +46 -0
- package/test/engines.test.js +11 -0
- package/test/get-latest-tag.test.js +51 -0
- package/test/git-precheck.test.js +44 -0
- package/test/github-final.test.js +93 -0
- package/test/glotpress-download.test.js +176 -0
- package/test/nested-path.test.js +39 -0
- package/test/no-fs-extra.test.js +11 -0
- package/test/package-identity.test.js +19 -0
- package/test/path-confine.test.js +42 -0
- package/test/pot-docs.test.js +27 -0
- package/test/pot-headers.test.js +49 -0
- package/test/project-root.test.js +26 -0
- package/test/release-waterfall.test.js +62 -0
- package/test/semver.test.js +25 -0
- package/test/settings-clone.test.js +34 -0
- package/test/settings-path.test.js +31 -0
- package/test/shell.test.js +100 -0
- package/test/singleton.test.js +14 -0
- package/test/stable-tag.test.js +13 -0
- package/test/stub-security.test.js +37 -0
- package/test/template-folder.test.js +19 -0
- package/test/translations.test.js +51 -0
- package/test/update-file-content.test.js +38 -0
- package/test/updates.test.js +39 -0
- package/test/version.test.js +22 -0
- package/test/view-escape.test.js +17 -0
- package/test/view.test.js +33 -0
- package/test/write-force.test.js +47 -0
- package/src/utilities/semver.js +0 -45
- package/template/.github/pull_request_template.md +0 -52
- package/template/configs/.editorconfig +0 -27
- package/template/configs/.eslintignore +0 -13
- package/template/configs/.eslintrc.js +0 -13
- package/template/configs/.phpcs.xml.dist +0 -87
- package/template/configs/.prettierignore +0 -49
- package/template/configs/.prettierrc.js +0 -3
- package/template/configs/.stylelintrc +0 -44
- package/template/configs/composer.json +0 -51
- package/template/configs/gitattributes +0 -34
- package/template/configs/gitignore +0 -43
- package/template/configs/package.json +0 -66
- package/template/configs/postcss.config.js +0 -6
- package/template/configs/tailwind.config.js +0 -8
- package/template/configs/webpack.mix.js +0 -65
- package/template/configs/webpack.mix.local.js +0 -3
- package/template/includes/index.php +0 -2
- package/template/index.js +0 -0
- package/template/index.php +0 -4
- package/template/plugin/assets/scss/app.scss +0 -0
- package/template/plugin/assets/src/app.js +0 -0
- package/template/tools/laravel-mix/wp-pot.js +0 -57
- package/template/tools/wp-glotpress.js +0 -162
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-004: githubFinalTasks must add an explicit file list, run hooks, and not push on failure.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { mkdtemp, rm, writeFile, mkdir } from 'node:fs/promises'
|
|
6
|
+
import { tmpdir } from 'node:os'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
import { mock, test, beforeEach } from 'node:test'
|
|
9
|
+
|
|
10
|
+
const execCalls = []
|
|
11
|
+
let execImpl = ( cmd, cb ) => cb( '', null, '' )
|
|
12
|
+
|
|
13
|
+
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
14
|
+
exports: {
|
|
15
|
+
execCommand( cmd, cb, options = {} ) {
|
|
16
|
+
execCalls.push( typeof cmd === 'string' ? cmd : cmd.join( ' ' ) )
|
|
17
|
+
execImpl( cmd, cb, options )
|
|
18
|
+
},
|
|
19
|
+
runCommand() {},
|
|
20
|
+
},
|
|
21
|
+
} )
|
|
22
|
+
|
|
23
|
+
const { pluginData } = await import( '../src/commands/release.js' )
|
|
24
|
+
const { githubFinalTasks } = await import( '../src/commands/release/github.js' )
|
|
25
|
+
|
|
26
|
+
function runFinal() {
|
|
27
|
+
return new Promise( ( resolve ) => {
|
|
28
|
+
githubFinalTasks( ( err ) => resolve( err ) )
|
|
29
|
+
} )
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
beforeEach( () => {
|
|
33
|
+
execCalls.length = 0
|
|
34
|
+
execImpl = ( cmd, cb ) => cb( '', null, '' )
|
|
35
|
+
pluginData.semver = { getNextVersionString: () => '1.2.3' }
|
|
36
|
+
pluginData.commitMessages = [ 'update readme.txt' ]
|
|
37
|
+
} )
|
|
38
|
+
|
|
39
|
+
async function withReleaseFiles( run ) {
|
|
40
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-' ) )
|
|
41
|
+
const cwd = process.cwd()
|
|
42
|
+
await writeFile( join( dir, 'wp.advads' ), '{}' )
|
|
43
|
+
await writeFile( join( dir, 'changelog.txt' ), '== Changelog ==\n' )
|
|
44
|
+
await writeFile( join( dir, 'readme.txt' ), 'Stable tag: 1.0.0\n' )
|
|
45
|
+
await writeFile( join( dir, 'advanced-ads.php' ), '<?php\n' )
|
|
46
|
+
await mkdir( join( dir, 'languages' ) )
|
|
47
|
+
process.chdir( dir )
|
|
48
|
+
try {
|
|
49
|
+
return await run()
|
|
50
|
+
} finally {
|
|
51
|
+
process.chdir( cwd )
|
|
52
|
+
await rm( dir, { recursive: true, force: true } )
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
test( 'does not git add the whole working tree', async () => {
|
|
57
|
+
await withReleaseFiles( async () => {
|
|
58
|
+
await runFinal()
|
|
59
|
+
assert.equal(
|
|
60
|
+
execCalls.some( ( cmd ) => /(^| )git add \.( |$)/.test( cmd ) ),
|
|
61
|
+
false
|
|
62
|
+
)
|
|
63
|
+
} )
|
|
64
|
+
} )
|
|
65
|
+
|
|
66
|
+
test( 'commit runs hooks instead of --no-verify', async () => {
|
|
67
|
+
await withReleaseFiles( async () => {
|
|
68
|
+
await runFinal()
|
|
69
|
+
const commit = execCalls.find( ( cmd ) => cmd.includes( 'git commit' ) )
|
|
70
|
+
assert.ok( commit, 'expected a git commit' )
|
|
71
|
+
assert.equal( commit.includes( '--no-verify' ), false )
|
|
72
|
+
} )
|
|
73
|
+
} )
|
|
74
|
+
|
|
75
|
+
test( 'does not push when commit fails', async () => {
|
|
76
|
+
await withReleaseFiles( async () => {
|
|
77
|
+
execImpl = ( cmd, cb ) => {
|
|
78
|
+
const line = typeof cmd === 'string' ? cmd : cmd.join( ' ' )
|
|
79
|
+
if ( line.includes( 'git commit' ) ) {
|
|
80
|
+
cb( '', new Error( 'hook failed' ), 'phpcs' )
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
cb( '', null, '' )
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const err = await runFinal()
|
|
87
|
+
assert.ok( err )
|
|
88
|
+
assert.equal(
|
|
89
|
+
execCalls.some( ( cmd ) => cmd.includes( 'git push' ) ),
|
|
90
|
+
false
|
|
91
|
+
)
|
|
92
|
+
} )
|
|
93
|
+
} )
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-011: GlotPress downloader returns { files, locales } in-process.
|
|
3
|
+
* Run with --test-isolation=none
|
|
4
|
+
*/
|
|
5
|
+
import assert from 'node:assert/strict'
|
|
6
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
7
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
8
|
+
import { tmpdir } from 'node:os'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { test } from 'node:test'
|
|
11
|
+
import { saveConfig } from '../src/utilities/settings.js'
|
|
12
|
+
import { downloadTranslations } from '../src/commands/wp-glotpress.js'
|
|
13
|
+
|
|
14
|
+
test( 'glotpress downloader is a function, not a class', () => {
|
|
15
|
+
const src = readFileSync( new URL( '../src/commands/wp-glotpress.js', import.meta.url ), 'utf8' )
|
|
16
|
+
assert.doesNotMatch( src, /class GlotPressDownloader/ )
|
|
17
|
+
} )
|
|
18
|
+
|
|
19
|
+
test( 'downloadTranslations returns files and locale names without scraping CLI stdout', async () => {
|
|
20
|
+
const dest = await mkdtemp( join( tmpdir(), 'wp-advads-glotpress-' ) )
|
|
21
|
+
const cwd = process.cwd()
|
|
22
|
+
process.chdir( dest )
|
|
23
|
+
saveConfig( {
|
|
24
|
+
glotpress: { project: 'my-plugin', destination: dest },
|
|
25
|
+
wpPot: { domain: 'myplugin' },
|
|
26
|
+
} )
|
|
27
|
+
|
|
28
|
+
const originalFetch = globalThis.fetch
|
|
29
|
+
globalThis.fetch = async ( url ) => {
|
|
30
|
+
const href = String( url )
|
|
31
|
+
if ( href.endsWith( '/api/projects/my-plugin' ) ) {
|
|
32
|
+
return {
|
|
33
|
+
ok: true,
|
|
34
|
+
json: async () => ( {
|
|
35
|
+
translation_sets: [
|
|
36
|
+
{
|
|
37
|
+
wp_locale: 'de_DE',
|
|
38
|
+
slug: 'default',
|
|
39
|
+
name: 'German',
|
|
40
|
+
locale: 'de',
|
|
41
|
+
current_count: 4,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
} ),
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { ok: true, text: async () => 'translation-bytes' }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const result = await downloadTranslations()
|
|
52
|
+
|
|
53
|
+
assert.ok( result )
|
|
54
|
+
assert.ok( Array.isArray( result.files ) )
|
|
55
|
+
assert.ok( result.files.some( ( file ) => file.endsWith( 'myplugin-de_DE.po' ) ) )
|
|
56
|
+
assert.ok( result.files.some( ( file ) => file.endsWith( 'myplugin-de_DE.mo' ) ) )
|
|
57
|
+
assert.equal(
|
|
58
|
+
Object.values( result.locales ).includes( 'German' ),
|
|
59
|
+
true
|
|
60
|
+
)
|
|
61
|
+
} finally {
|
|
62
|
+
globalThis.fetch = originalFetch
|
|
63
|
+
process.chdir( cwd )
|
|
64
|
+
await rm( dest, { recursive: true, force: true } )
|
|
65
|
+
}
|
|
66
|
+
} )
|
|
67
|
+
|
|
68
|
+
async function downloadWithSets( dest, sets, fileResponse ) {
|
|
69
|
+
const cwd = process.cwd()
|
|
70
|
+
process.chdir( dest )
|
|
71
|
+
saveConfig( {
|
|
72
|
+
glotpress: { project: 'my-plugin', destination: dest },
|
|
73
|
+
wpPot: { domain: 'myplugin' },
|
|
74
|
+
} )
|
|
75
|
+
|
|
76
|
+
const originalFetch = globalThis.fetch
|
|
77
|
+
globalThis.fetch = async ( url ) => {
|
|
78
|
+
const href = String( url )
|
|
79
|
+
if ( href.endsWith( '/api/projects/my-plugin' ) ) {
|
|
80
|
+
return {
|
|
81
|
+
ok: true,
|
|
82
|
+
json: async () => ( { translation_sets: sets } ),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return fileResponse
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
return await downloadTranslations()
|
|
90
|
+
} finally {
|
|
91
|
+
globalThis.fetch = originalFetch
|
|
92
|
+
process.chdir( cwd )
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
test( 'downloadTranslations skips locale ids that are not alphanumeric', async () => {
|
|
97
|
+
const dest = await mkdtemp( join( tmpdir(), 'wp-advads-glotpress-' ) )
|
|
98
|
+
try {
|
|
99
|
+
await downloadWithSets( dest, [
|
|
100
|
+
{
|
|
101
|
+
wp_locale: '../evil',
|
|
102
|
+
slug: 'default',
|
|
103
|
+
name: 'Evil',
|
|
104
|
+
locale: 'de',
|
|
105
|
+
current_count: 4,
|
|
106
|
+
},
|
|
107
|
+
], { ok: true, text: async () => 'translation-bytes' } )
|
|
108
|
+
|
|
109
|
+
assert.equal( existsSync( join( dest, '..', 'evil.po' ) ), false )
|
|
110
|
+
assert.equal( existsSync( join( dest, '..', 'evil.mo' ) ), false )
|
|
111
|
+
} finally {
|
|
112
|
+
await rm( dest, { recursive: true, force: true } )
|
|
113
|
+
}
|
|
114
|
+
} )
|
|
115
|
+
|
|
116
|
+
test( 'downloadTranslations does not write HTML error bodies as po/mo files', async () => {
|
|
117
|
+
const dest = await mkdtemp( join( tmpdir(), 'wp-advads-glotpress-' ) )
|
|
118
|
+
try {
|
|
119
|
+
const result = await downloadWithSets( dest, [
|
|
120
|
+
{
|
|
121
|
+
wp_locale: 'de_DE',
|
|
122
|
+
slug: 'default',
|
|
123
|
+
name: 'German',
|
|
124
|
+
locale: 'de',
|
|
125
|
+
current_count: 4,
|
|
126
|
+
},
|
|
127
|
+
], { ok: false, text: async () => '<html>nope</html>' } )
|
|
128
|
+
|
|
129
|
+
assert.equal( existsSync( join( dest, 'myplugin-de_DE.po' ) ), false )
|
|
130
|
+
assert.equal( existsSync( join( dest, 'myplugin-de_DE.mo' ) ), false )
|
|
131
|
+
assert.deepEqual( result.files, [] )
|
|
132
|
+
} finally {
|
|
133
|
+
await rm( dest, { recursive: true, force: true } )
|
|
134
|
+
}
|
|
135
|
+
} )
|
|
136
|
+
|
|
137
|
+
test( 'downloadTranslations creates nested destination directories', async () => {
|
|
138
|
+
const dest = await mkdtemp( join( tmpdir(), 'wp-advads-glotpress-' ) )
|
|
139
|
+
const nested = join( dest, 'languages', 'i18n' )
|
|
140
|
+
const cwd = process.cwd()
|
|
141
|
+
process.chdir( dest )
|
|
142
|
+
saveConfig( {
|
|
143
|
+
glotpress: { project: 'my-plugin', destination: nested },
|
|
144
|
+
wpPot: { domain: 'myplugin' },
|
|
145
|
+
} )
|
|
146
|
+
|
|
147
|
+
const originalFetch = globalThis.fetch
|
|
148
|
+
globalThis.fetch = async ( url ) => {
|
|
149
|
+
if ( String( url ).endsWith( '/api/projects/my-plugin' ) ) {
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
json: async () => ( {
|
|
153
|
+
translation_sets: [
|
|
154
|
+
{
|
|
155
|
+
wp_locale: 'de_DE',
|
|
156
|
+
slug: 'default',
|
|
157
|
+
name: 'German',
|
|
158
|
+
locale: 'de',
|
|
159
|
+
current_count: 4,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
} ),
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { ok: true, text: async () => 'translation-bytes' }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
await downloadTranslations()
|
|
170
|
+
assert.equal( existsSync( join( nested, 'myplugin-de_DE.po' ) ), true )
|
|
171
|
+
} finally {
|
|
172
|
+
globalThis.fetch = originalFetch
|
|
173
|
+
process.chdir( cwd )
|
|
174
|
+
await rm( dest, { recursive: true, force: true } )
|
|
175
|
+
}
|
|
176
|
+
} )
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-032: js, css, and view nested names accept / and \\.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { existsSync } from 'node:fs'
|
|
6
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
7
|
+
import { tmpdir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { test } from 'node:test'
|
|
10
|
+
import css from '../src/commands/css.js'
|
|
11
|
+
import javascript from '../src/commands/javascript.js'
|
|
12
|
+
import views from '../src/commands/views.js'
|
|
13
|
+
|
|
14
|
+
test( 'js css and view nested paths accept slash and backslash', async () => {
|
|
15
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-nested-' ) )
|
|
16
|
+
const cwd = process.cwd()
|
|
17
|
+
process.chdir( dir )
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await writeFile( join( dir, 'wp.advads' ), JSON.stringify( {
|
|
21
|
+
paths: { views: 'views', javascript: 'assets/src', css: 'assets/css' },
|
|
22
|
+
wp: { version: '1.0.0' },
|
|
23
|
+
misc: { package: 'AdvancedAds' },
|
|
24
|
+
author: { name: 'Test', email: 'test@example.com' },
|
|
25
|
+
} ) )
|
|
26
|
+
|
|
27
|
+
javascript( 'Admin/App', {} )
|
|
28
|
+
assert.equal( existsSync( join( dir, 'assets', 'src', 'admin', 'app.js' ) ), true )
|
|
29
|
+
|
|
30
|
+
css( 'Admin/App', {} )
|
|
31
|
+
assert.equal( existsSync( join( dir, 'assets', 'css', 'admin', 'app.css' ) ), true )
|
|
32
|
+
|
|
33
|
+
views( 'Admin\\Box' )
|
|
34
|
+
assert.equal( existsSync( join( dir, 'views', 'admin', 'box.php' ) ), true )
|
|
35
|
+
} finally {
|
|
36
|
+
process.chdir( cwd )
|
|
37
|
+
await rm( dir, { recursive: true, force: true } )
|
|
38
|
+
}
|
|
39
|
+
} )
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-040: drop fs-extra; stdlib mkdir/write is enough.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { readFileSync } from 'node:fs'
|
|
6
|
+
import { test } from 'node:test'
|
|
7
|
+
|
|
8
|
+
test( 'package.json does not depend on fs-extra', () => {
|
|
9
|
+
const pkg = JSON.parse( readFileSync( new URL( '../package.json', import.meta.url ), 'utf8' ) )
|
|
10
|
+
assert.equal( pkg.dependencies['fs-extra'], undefined )
|
|
11
|
+
} )
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-057: package description grammar/capitalization; no StandardJS claim without tooling.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { readFileSync } from 'node:fs'
|
|
6
|
+
import { test } from 'node:test'
|
|
7
|
+
|
|
8
|
+
test( 'package.json description uses WordPress and correct article', () => {
|
|
9
|
+
const { description } = JSON.parse( readFileSync( new URL( '../package.json', import.meta.url ), 'utf8' ) )
|
|
10
|
+
assert.doesNotMatch( description, /wordPress/ )
|
|
11
|
+
assert.doesNotMatch( description, /Create a Advanced/ )
|
|
12
|
+
assert.doesNotMatch( description, /eco system/ )
|
|
13
|
+
assert.match( description, /WordPress/ )
|
|
14
|
+
} )
|
|
15
|
+
|
|
16
|
+
test( 'README does not claim StandardJS', () => {
|
|
17
|
+
const readme = readFileSync( new URL( '../README.md', import.meta.url ), 'utf8' )
|
|
18
|
+
assert.doesNotMatch( readme, /StandardJS/i )
|
|
19
|
+
} )
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-013: scaffold writes must stay inside the project root.
|
|
3
|
+
* Run with --test-isolation=none
|
|
4
|
+
*/
|
|
5
|
+
import assert from 'node:assert/strict'
|
|
6
|
+
import { existsSync } from 'node:fs'
|
|
7
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
8
|
+
import { tmpdir } from 'node:os'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { test } from 'node:test'
|
|
11
|
+
import views from '../src/commands/views.js'
|
|
12
|
+
import { writeFile as writeScaffold } from '../src/utilities/filesystem.js'
|
|
13
|
+
|
|
14
|
+
test( 'scaffold writes stay inside the project root', async () => {
|
|
15
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-path-' ) )
|
|
16
|
+
const project = join( dir, 'project' )
|
|
17
|
+
const outside = join( dir, 'outside' )
|
|
18
|
+
await mkdir( project )
|
|
19
|
+
await mkdir( outside )
|
|
20
|
+
await writeFile( join( project, 'wp.advads' ), JSON.stringify( {
|
|
21
|
+
paths: { views: 'views' },
|
|
22
|
+
wp: { version: '1.0.0' },
|
|
23
|
+
misc: { package: 'AdvancedAds' },
|
|
24
|
+
author: { name: 'Test', email: 'test@example.com' },
|
|
25
|
+
} ) )
|
|
26
|
+
|
|
27
|
+
const cwd = process.cwd()
|
|
28
|
+
process.chdir( project )
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
assert.throws( () => views( '../../outside/pwned' ) )
|
|
32
|
+
assert.equal( existsSync( join( outside, 'pwned.php' ) ), false )
|
|
33
|
+
|
|
34
|
+
assert.throws( () => writeScaffold( join( project, 'views' ), 'foo\0.php', 'nope' ) )
|
|
35
|
+
|
|
36
|
+
assert.throws( () => writeScaffold( join( project, 'views' ), join( outside, 'pwned.php' ), 'nope' ) )
|
|
37
|
+
assert.equal( existsSync( join( outside, 'pwned.php' ) ), false )
|
|
38
|
+
} finally {
|
|
39
|
+
process.chdir( cwd )
|
|
40
|
+
await rm( dir, { recursive: true, force: true } )
|
|
41
|
+
}
|
|
42
|
+
} )
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-030: pot creates a .pot; docs and CLI copy must not claim po/mo.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { readFileSync } from 'node:fs'
|
|
6
|
+
import { test } from 'node:test'
|
|
7
|
+
|
|
8
|
+
test( 'pot command and docs describe a .pot, not po/mo files', () => {
|
|
9
|
+
const pot = readFileSync( new URL( '../src/commands/wp-pot.js', import.meta.url ), 'utf8' )
|
|
10
|
+
assert.match( pot, /make-pot/ )
|
|
11
|
+
assert.doesNotMatch( pot, /make-mo|make-json/ )
|
|
12
|
+
assert.doesNotMatch( pot, /PO and MO/ )
|
|
13
|
+
|
|
14
|
+
const app = readFileSync( new URL( '../src/app.js', import.meta.url ), 'utf8' )
|
|
15
|
+
const potCmd = app.split( ".command('pot')" )[1].split( '.command(' )[0]
|
|
16
|
+
assert.match( potCmd, /\.pot/ )
|
|
17
|
+
assert.doesNotMatch( potCmd, /\bpo\b|\bmo\b/i )
|
|
18
|
+
|
|
19
|
+
const readme = readFileSync( new URL( '../README.md', import.meta.url ), 'utf8' )
|
|
20
|
+
const potSection = readme.split( '#### `pot`' )[1].split( '####' )[0]
|
|
21
|
+
assert.match( potSection, /\.pot/ )
|
|
22
|
+
assert.doesNotMatch( potSection, /\.po\b|\.mo\b/ )
|
|
23
|
+
|
|
24
|
+
const changelog = readFileSync( new URL( '../CHANGELOG.md', import.meta.url ), 'utf8' )
|
|
25
|
+
assert.match( changelog, /`pot` command to generate `\.pot`/ )
|
|
26
|
+
assert.doesNotMatch( changelog, /`pot` command to generate `\.po` and `\.mo`/ )
|
|
27
|
+
} )
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-038: pot headers come from wp.advads author, not a hardcoded name.
|
|
3
|
+
* Run with --experimental-test-module-mocks
|
|
4
|
+
*/
|
|
5
|
+
import assert from 'node:assert/strict'
|
|
6
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
7
|
+
import { tmpdir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { mock, test } from 'node:test'
|
|
10
|
+
|
|
11
|
+
const potArgs = []
|
|
12
|
+
|
|
13
|
+
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
14
|
+
exports: {
|
|
15
|
+
execCommand() {},
|
|
16
|
+
runCommand( cmd, args, cb ) {
|
|
17
|
+
potArgs.push( { cmd, args } )
|
|
18
|
+
cb()
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
} )
|
|
22
|
+
|
|
23
|
+
const { default: wpPot } = await import( '../src/commands/wp-pot.js' )
|
|
24
|
+
const { saveConfig } = await import( '../src/utilities/settings.js' )
|
|
25
|
+
|
|
26
|
+
test( 'pot Last-Translator and Language-Team use author from config', async () => {
|
|
27
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-pot-' ) )
|
|
28
|
+
const cwd = process.cwd()
|
|
29
|
+
process.chdir( dir )
|
|
30
|
+
potArgs.length = 0
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
saveConfig( {
|
|
34
|
+
author: { name: 'Jane Doe', email: 'jane@example.com' },
|
|
35
|
+
wpPot: { output: '/languages/', file: 'plugin.pot', domain: 'plugin' },
|
|
36
|
+
} )
|
|
37
|
+
|
|
38
|
+
await new Promise( ( resolve ) => wpPot( resolve ) )
|
|
39
|
+
|
|
40
|
+
const headersArg = potArgs[0].args.find( ( arg ) => arg.startsWith( '--headers=' ) )
|
|
41
|
+
const headers = JSON.parse( headersArg.slice( '--headers='.length ) )
|
|
42
|
+
assert.equal( headers['Last-Translator'], 'Jane Doe <jane@example.com>' )
|
|
43
|
+
assert.equal( headers['Language-Team'], 'Jane Doe <jane@example.com>' )
|
|
44
|
+
assert.doesNotMatch( headersArg, /Shakeeb Ahmed/ )
|
|
45
|
+
} finally {
|
|
46
|
+
process.chdir( cwd )
|
|
47
|
+
await rm( dir, { recursive: true, force: true } )
|
|
48
|
+
}
|
|
49
|
+
} )
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-015: a failed getProjectRoot walk must not cache the filesystem root.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
6
|
+
import { tmpdir } from 'node:os'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
import { test } from 'node:test'
|
|
9
|
+
import { getProjectRoot } from '../src/utilities/filesystem.js'
|
|
10
|
+
|
|
11
|
+
test( 'failed getProjectRoot does not make a later lookup return the filesystem root', async () => {
|
|
12
|
+
const empty = await mkdtemp( join( tmpdir(), 'wp-advads-empty-' ) )
|
|
13
|
+
const project = await mkdtemp( join( tmpdir(), 'wp-advads-root-' ) )
|
|
14
|
+
await writeFile( join( project, 'wp.advads' ), '{}' )
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
assert.throws(
|
|
18
|
+
() => getProjectRoot( empty ),
|
|
19
|
+
/Project root not found/
|
|
20
|
+
)
|
|
21
|
+
assert.equal( getProjectRoot( project ), project )
|
|
22
|
+
} finally {
|
|
23
|
+
await rm( empty, { recursive: true, force: true } )
|
|
24
|
+
await rm( project, { recursive: true, force: true } )
|
|
25
|
+
}
|
|
26
|
+
} )
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-003: release changelog and version waterfalls must pass errors to next.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
6
|
+
import { tmpdir } from 'node:os'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
import { mock, test } from 'node:test'
|
|
9
|
+
|
|
10
|
+
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
11
|
+
exports: {
|
|
12
|
+
execCommand( cmd, cb ) {
|
|
13
|
+
cb( '', null, '' )
|
|
14
|
+
},
|
|
15
|
+
runCommand() {},
|
|
16
|
+
},
|
|
17
|
+
} )
|
|
18
|
+
|
|
19
|
+
const { pluginData } = await import( '../src/commands/release.js' )
|
|
20
|
+
const { default: updateChangelog } = await import( '../src/commands/release/changelog.js' )
|
|
21
|
+
const { default: updateVersionNumber } = await import( '../src/commands/release/change-version.js' )
|
|
22
|
+
|
|
23
|
+
async function inTempDir( run ) {
|
|
24
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-' ) )
|
|
25
|
+
const cwd = process.cwd()
|
|
26
|
+
process.chdir( dir )
|
|
27
|
+
try {
|
|
28
|
+
return await run()
|
|
29
|
+
} finally {
|
|
30
|
+
process.chdir( cwd )
|
|
31
|
+
await rm( dir, { recursive: true, force: true } )
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test( 'updateChangelog passes a missing .changelog directory to next', async () => {
|
|
36
|
+
await inTempDir( async () => {
|
|
37
|
+
pluginData.semver = { preRelease: true }
|
|
38
|
+
pluginData.glotpress = { destination: './languages/' }
|
|
39
|
+
pluginData.locales = {}
|
|
40
|
+
|
|
41
|
+
const err = await new Promise( ( resolve ) => {
|
|
42
|
+
updateChangelog( ( error ) => resolve( error ) )
|
|
43
|
+
} )
|
|
44
|
+
|
|
45
|
+
assert.ok( err, 'expected changelog failure to reach next' )
|
|
46
|
+
} )
|
|
47
|
+
} )
|
|
48
|
+
|
|
49
|
+
test( 'updateVersionNumber passes missing PHP files to next', async () => {
|
|
50
|
+
await inTempDir( async () => {
|
|
51
|
+
pluginData.semver = {
|
|
52
|
+
preRelease: true,
|
|
53
|
+
getNextVersionString: () => '1.2.3',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const err = await new Promise( ( resolve ) => {
|
|
57
|
+
updateVersionNumber( ( error ) => resolve( error ) )
|
|
58
|
+
} )
|
|
59
|
+
|
|
60
|
+
assert.ok( err, 'expected version-update failure to reach next' )
|
|
61
|
+
} )
|
|
62
|
+
} )
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-009: SemVer parse, pre-count, and JS isset replacement.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { test } from 'node:test'
|
|
6
|
+
import { SemVer } from '../src/commands/release/semver.js'
|
|
7
|
+
|
|
8
|
+
test( 'same-line alpha pre-release increments the count past 2', () => {
|
|
9
|
+
const semver = new SemVer( '1.2.0-alpha.2', 'patch', 'alpha' )
|
|
10
|
+
assert.equal( semver.getNextVersionString(), '1.2.0-alpha.3' )
|
|
11
|
+
} )
|
|
12
|
+
|
|
13
|
+
test( 'same-line alpha.1 becomes alpha.2', () => {
|
|
14
|
+
const semver = new SemVer( '1.2.0-alpha.1', 'patch', 'alpha' )
|
|
15
|
+
assert.equal( semver.getNextVersionString(), '1.2.0-alpha.2' )
|
|
16
|
+
} )
|
|
17
|
+
|
|
18
|
+
test( 'invalid version throws instead of crashing on .groups', () => {
|
|
19
|
+
assert.throws( () => new SemVer( 'not-a-version', 'patch', false ), /failed/ )
|
|
20
|
+
} )
|
|
21
|
+
|
|
22
|
+
test( 'switching alpha to beta on the same numbers does not throw', () => {
|
|
23
|
+
const semver = new SemVer( '1.2.1-alpha.1', 'patch', 'beta' )
|
|
24
|
+
assert.equal( semver.getNextVersionString(), '1.2.1-beta.1' )
|
|
25
|
+
} )
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-018: scaffold commands must not mutate the cached getSetting() object.
|
|
3
|
+
* Run with --test-isolation=none
|
|
4
|
+
*/
|
|
5
|
+
import assert from 'node:assert/strict'
|
|
6
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
7
|
+
import { tmpdir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { test } from 'node:test'
|
|
10
|
+
import { getSetting } from '../src/utilities/index.js'
|
|
11
|
+
import views from '../src/commands/views.js'
|
|
12
|
+
|
|
13
|
+
test( 'view command does not persist heading onto the cached settings', async () => {
|
|
14
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-clone-' ) )
|
|
15
|
+
const cwd = process.cwd()
|
|
16
|
+
process.chdir( dir )
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await writeFile( join( dir, 'wp.advads' ), JSON.stringify( {
|
|
20
|
+
paths: { views: 'views' },
|
|
21
|
+
wp: { version: '1.0.0' },
|
|
22
|
+
misc: { package: 'AdvancedAds' },
|
|
23
|
+
author: { name: 'Test', email: 'test@example.com' },
|
|
24
|
+
} ) )
|
|
25
|
+
|
|
26
|
+
views( 'sidebar', 'Sidebar Heading' )
|
|
27
|
+
|
|
28
|
+
assert.equal( getSetting().heading, undefined )
|
|
29
|
+
assert.equal( getSetting().description, undefined )
|
|
30
|
+
} finally {
|
|
31
|
+
process.chdir( cwd )
|
|
32
|
+
await rm( dir, { recursive: true, force: true } )
|
|
33
|
+
}
|
|
34
|
+
} )
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-016: saveConfig and readConfigFile must use the same wp.advads path.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert/strict'
|
|
5
|
+
import { existsSync } from 'node:fs'
|
|
6
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
7
|
+
import { tmpdir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { test } from 'node:test'
|
|
10
|
+
import { saveConfig, readConfigFile } from '../src/utilities/settings.js'
|
|
11
|
+
|
|
12
|
+
test( 'saveConfig from a subdirectory writes the discovered wp.advads', async () => {
|
|
13
|
+
const project = await mkdtemp( join( tmpdir(), 'wp-advads-cfg-' ) )
|
|
14
|
+
const sub = join( project, 'includes' )
|
|
15
|
+
await mkdir( sub )
|
|
16
|
+
await writeFile( join( project, 'wp.advads' ), JSON.stringify( { keep: true } ) )
|
|
17
|
+
|
|
18
|
+
const cwd = process.cwd()
|
|
19
|
+
process.chdir( sub )
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
saveConfig( { keep: false } )
|
|
23
|
+
assert.equal( existsSync( join( sub, 'wp.advads' ) ), false )
|
|
24
|
+
const saved = JSON.parse( await readFile( join( project, 'wp.advads' ), 'utf8' ) )
|
|
25
|
+
assert.equal( saved.keep, false )
|
|
26
|
+
assert.equal( readConfigFile().keep, false )
|
|
27
|
+
} finally {
|
|
28
|
+
process.chdir( cwd )
|
|
29
|
+
await rm( project, { recursive: true, force: true } )
|
|
30
|
+
}
|
|
31
|
+
} )
|