shopify-starter-kit 1.0.0
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/.agent/skills/shopify-apps/SKILL.md +47 -0
- package/.agent/skills/shopify-automation/SKILL.md +172 -0
- package/.agent/skills/shopify-development/README.md +60 -0
- package/.agent/skills/shopify-development/SKILL.md +368 -0
- package/.agent/skills/shopify-development/references/app-development.md +578 -0
- package/.agent/skills/shopify-development/references/extensions.md +555 -0
- package/.agent/skills/shopify-development/references/themes.md +498 -0
- package/.agent/skills/shopify-development/scripts/requirements.txt +19 -0
- package/.agent/skills/shopify-development/scripts/shopify_graphql.py +428 -0
- package/.agent/skills/shopify-development/scripts/shopify_init.py +441 -0
- package/.agent/skills/shopify-development/scripts/tests/test_shopify_init.py +379 -0
- package/bin/cli.js +3 -0
- package/package.json +32 -0
- package/src/index.js +116 -0
- package/templates/.agent/skills/shopify-apps/SKILL.md +47 -0
- package/templates/.agent/skills/shopify-automation/SKILL.md +172 -0
- package/templates/.agent/skills/shopify-development/README.md +60 -0
- package/templates/.agent/skills/shopify-development/SKILL.md +368 -0
- package/templates/.agent/skills/shopify-development/references/app-development.md +578 -0
- package/templates/.agent/skills/shopify-development/references/extensions.md +555 -0
- package/templates/.agent/skills/shopify-development/references/themes.md +498 -0
- package/templates/.agent/skills/shopify-development/scripts/requirements.txt +19 -0
- package/templates/.agent/skills/shopify-development/scripts/shopify_graphql.py +428 -0
- package/templates/.agent/skills/shopify-development/scripts/shopify_init.py +441 -0
- package/templates/.agent/skills/shopify-development/scripts/tests/test_shopify_init.py +379 -0
- package/templates/.devcontainer/devcontainer.json +27 -0
- package/templates/tests/playwright.config.ts +26 -0
- package/templates/tests/vitest.config.ts +9 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for shopify_init.py
|
|
3
|
+
|
|
4
|
+
Run with: pytest test_shopify_init.py -v --cov=shopify_init --cov-report=term-missing
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import pytest
|
|
11
|
+
import subprocess
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from unittest.mock import Mock, patch, mock_open, MagicMock
|
|
14
|
+
|
|
15
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
16
|
+
|
|
17
|
+
from shopify_init import EnvLoader, EnvConfig, ShopifyInitializer
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestEnvLoader:
|
|
21
|
+
"""Test EnvLoader class."""
|
|
22
|
+
|
|
23
|
+
def test_load_env_file_success(self, tmp_path):
|
|
24
|
+
"""Test loading valid .env file."""
|
|
25
|
+
env_file = tmp_path / ".env"
|
|
26
|
+
env_file.write_text("""
|
|
27
|
+
SHOPIFY_API_KEY=test_key
|
|
28
|
+
SHOPIFY_API_SECRET=test_secret
|
|
29
|
+
SHOP_DOMAIN=test.myshopify.com
|
|
30
|
+
# Comment line
|
|
31
|
+
SCOPES=read_products,write_products
|
|
32
|
+
""")
|
|
33
|
+
|
|
34
|
+
result = EnvLoader.load_env_file(env_file)
|
|
35
|
+
|
|
36
|
+
assert result['SHOPIFY_API_KEY'] == 'test_key'
|
|
37
|
+
assert result['SHOPIFY_API_SECRET'] == 'test_secret'
|
|
38
|
+
assert result['SHOP_DOMAIN'] == 'test.myshopify.com'
|
|
39
|
+
assert result['SCOPES'] == 'read_products,write_products'
|
|
40
|
+
|
|
41
|
+
def test_load_env_file_with_quotes(self, tmp_path):
|
|
42
|
+
"""Test loading .env file with quoted values."""
|
|
43
|
+
env_file = tmp_path / ".env"
|
|
44
|
+
env_file.write_text("""
|
|
45
|
+
SHOPIFY_API_KEY="test_key"
|
|
46
|
+
SHOPIFY_API_SECRET='test_secret'
|
|
47
|
+
""")
|
|
48
|
+
|
|
49
|
+
result = EnvLoader.load_env_file(env_file)
|
|
50
|
+
|
|
51
|
+
assert result['SHOPIFY_API_KEY'] == 'test_key'
|
|
52
|
+
assert result['SHOPIFY_API_SECRET'] == 'test_secret'
|
|
53
|
+
|
|
54
|
+
def test_load_env_file_nonexistent(self, tmp_path):
|
|
55
|
+
"""Test loading non-existent .env file."""
|
|
56
|
+
result = EnvLoader.load_env_file(tmp_path / "nonexistent.env")
|
|
57
|
+
assert result == {}
|
|
58
|
+
|
|
59
|
+
def test_load_env_file_invalid_format(self, tmp_path):
|
|
60
|
+
"""Test loading .env file with invalid lines."""
|
|
61
|
+
env_file = tmp_path / ".env"
|
|
62
|
+
env_file.write_text("""
|
|
63
|
+
VALID_KEY=value
|
|
64
|
+
INVALID_LINE_NO_EQUALS
|
|
65
|
+
ANOTHER_VALID=test
|
|
66
|
+
""")
|
|
67
|
+
|
|
68
|
+
result = EnvLoader.load_env_file(env_file)
|
|
69
|
+
|
|
70
|
+
assert result['VALID_KEY'] == 'value'
|
|
71
|
+
assert result['ANOTHER_VALID'] == 'test'
|
|
72
|
+
assert 'INVALID_LINE_NO_EQUALS' not in result
|
|
73
|
+
|
|
74
|
+
def test_get_env_paths(self, tmp_path):
|
|
75
|
+
"""Test getting .env file paths from universal directory structure."""
|
|
76
|
+
# Create directory structure (works with .agent, .claude, .gemini, .cursor)
|
|
77
|
+
agent_dir = tmp_path / ".agent"
|
|
78
|
+
skills_dir = agent_dir / "skills"
|
|
79
|
+
skill_dir = skills_dir / "shopify"
|
|
80
|
+
|
|
81
|
+
skill_dir.mkdir(parents=True)
|
|
82
|
+
|
|
83
|
+
# Create .env files at each level
|
|
84
|
+
(skill_dir / ".env").write_text("SKILL=1")
|
|
85
|
+
(skills_dir / ".env").write_text("SKILLS=1")
|
|
86
|
+
(agent_dir / ".env").write_text("AGENT=1")
|
|
87
|
+
|
|
88
|
+
paths = EnvLoader.get_env_paths(skill_dir)
|
|
89
|
+
|
|
90
|
+
assert len(paths) == 3
|
|
91
|
+
assert skill_dir / ".env" in paths
|
|
92
|
+
assert skills_dir / ".env" in paths
|
|
93
|
+
assert agent_dir / ".env" in paths
|
|
94
|
+
|
|
95
|
+
def test_load_config_priority(self, tmp_path, monkeypatch):
|
|
96
|
+
"""Test configuration loading priority across different AI tool directories."""
|
|
97
|
+
skill_dir = tmp_path / "skill"
|
|
98
|
+
skills_dir = tmp_path
|
|
99
|
+
agent_dir = tmp_path.parent # Could be .agent, .claude, .gemini, .cursor
|
|
100
|
+
|
|
101
|
+
skill_dir.mkdir(parents=True)
|
|
102
|
+
|
|
103
|
+
(skill_dir / ".env").write_text("SHOPIFY_API_KEY=skill_key")
|
|
104
|
+
(skills_dir / ".env").write_text("SHOPIFY_API_KEY=skills_key\nSHOP_DOMAIN=skills.myshopify.com")
|
|
105
|
+
|
|
106
|
+
monkeypatch.setenv("SHOPIFY_API_KEY", "process_key")
|
|
107
|
+
|
|
108
|
+
config = EnvLoader.load_config(skill_dir)
|
|
109
|
+
|
|
110
|
+
assert config.shopify_api_key == "process_key"
|
|
111
|
+
# Shop domain from skills/.env
|
|
112
|
+
assert config.shop_domain == "skills.myshopify.com"
|
|
113
|
+
|
|
114
|
+
def test_load_config_no_files(self, tmp_path):
|
|
115
|
+
"""Test configuration loading with no .env files."""
|
|
116
|
+
config = EnvLoader.load_config(tmp_path)
|
|
117
|
+
|
|
118
|
+
assert config.shopify_api_key is None
|
|
119
|
+
assert config.shopify_api_secret is None
|
|
120
|
+
assert config.shop_domain is None
|
|
121
|
+
assert config.scopes is None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class TestShopifyInitializer:
|
|
125
|
+
"""Test ShopifyInitializer class."""
|
|
126
|
+
|
|
127
|
+
@pytest.fixture
|
|
128
|
+
def config(self):
|
|
129
|
+
"""Create test config."""
|
|
130
|
+
return EnvConfig(
|
|
131
|
+
shopify_api_key="test_key",
|
|
132
|
+
shopify_api_secret="test_secret",
|
|
133
|
+
shop_domain="test.myshopify.com",
|
|
134
|
+
scopes="read_products,write_products"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
@pytest.fixture
|
|
138
|
+
def initializer(self, config):
|
|
139
|
+
"""Create initializer instance."""
|
|
140
|
+
return ShopifyInitializer(config)
|
|
141
|
+
|
|
142
|
+
def test_prompt_with_default(self, initializer):
|
|
143
|
+
"""Test prompt with default value."""
|
|
144
|
+
with patch('builtins.input', return_value=''):
|
|
145
|
+
result = initializer.prompt("Test", "default_value")
|
|
146
|
+
assert result == "default_value"
|
|
147
|
+
|
|
148
|
+
def test_prompt_with_input(self, initializer):
|
|
149
|
+
"""Test prompt with user input."""
|
|
150
|
+
with patch('builtins.input', return_value='user_input'):
|
|
151
|
+
result = initializer.prompt("Test", "default_value")
|
|
152
|
+
assert result == "user_input"
|
|
153
|
+
|
|
154
|
+
def test_select_option_valid(self, initializer):
|
|
155
|
+
"""Test select option with valid choice."""
|
|
156
|
+
options = ['app', 'extension', 'theme']
|
|
157
|
+
with patch('builtins.input', return_value='2'):
|
|
158
|
+
result = initializer.select_option("Choose", options)
|
|
159
|
+
assert result == 'extension'
|
|
160
|
+
|
|
161
|
+
def test_select_option_invalid_then_valid(self, initializer):
|
|
162
|
+
"""Test select option with invalid then valid choice."""
|
|
163
|
+
options = ['app', 'extension']
|
|
164
|
+
with patch('builtins.input', side_effect=['5', 'invalid', '1']):
|
|
165
|
+
result = initializer.select_option("Choose", options)
|
|
166
|
+
assert result == 'app'
|
|
167
|
+
|
|
168
|
+
def test_check_cli_installed_success(self, initializer):
|
|
169
|
+
"""Test CLI installed check - success."""
|
|
170
|
+
mock_result = Mock()
|
|
171
|
+
mock_result.returncode = 0
|
|
172
|
+
|
|
173
|
+
with patch('subprocess.run', return_value=mock_result):
|
|
174
|
+
assert initializer.check_cli_installed() is True
|
|
175
|
+
|
|
176
|
+
def test_check_cli_installed_failure(self, initializer):
|
|
177
|
+
"""Test CLI installed check - failure."""
|
|
178
|
+
with patch('subprocess.run', side_effect=FileNotFoundError):
|
|
179
|
+
assert initializer.check_cli_installed() is False
|
|
180
|
+
|
|
181
|
+
def test_create_app_config(self, initializer, tmp_path):
|
|
182
|
+
"""Test creating app configuration file."""
|
|
183
|
+
initializer.create_app_config(tmp_path, "test-app", "read_products")
|
|
184
|
+
|
|
185
|
+
config_file = tmp_path / "shopify.app.toml"
|
|
186
|
+
assert config_file.exists()
|
|
187
|
+
|
|
188
|
+
content = config_file.read_text()
|
|
189
|
+
assert 'name = "test-app"' in content
|
|
190
|
+
assert 'scopes = "read_products"' in content
|
|
191
|
+
assert 'client_id = "test_key"' in content
|
|
192
|
+
|
|
193
|
+
def test_create_extension_config(self, initializer, tmp_path):
|
|
194
|
+
"""Test creating extension configuration file."""
|
|
195
|
+
initializer.create_extension_config(tmp_path, "test-ext", "checkout")
|
|
196
|
+
|
|
197
|
+
config_file = tmp_path / "shopify.extension.toml"
|
|
198
|
+
assert config_file.exists()
|
|
199
|
+
|
|
200
|
+
content = config_file.read_text()
|
|
201
|
+
assert 'name = "test-ext"' in content
|
|
202
|
+
assert 'purchase.checkout.block.render' in content
|
|
203
|
+
|
|
204
|
+
def test_create_extension_config_admin_action(self, initializer, tmp_path):
|
|
205
|
+
"""Test creating admin action extension config."""
|
|
206
|
+
initializer.create_extension_config(tmp_path, "admin-ext", "admin_action")
|
|
207
|
+
|
|
208
|
+
config_file = tmp_path / "shopify.extension.toml"
|
|
209
|
+
content = config_file.read_text()
|
|
210
|
+
assert 'admin.product-details.action.render' in content
|
|
211
|
+
|
|
212
|
+
def test_create_readme(self, initializer, tmp_path):
|
|
213
|
+
"""Test creating README file."""
|
|
214
|
+
initializer.create_readme(tmp_path, "app", "Test App")
|
|
215
|
+
|
|
216
|
+
readme_file = tmp_path / "README.md"
|
|
217
|
+
assert readme_file.exists()
|
|
218
|
+
|
|
219
|
+
content = readme_file.read_text()
|
|
220
|
+
assert '# Test App' in content
|
|
221
|
+
assert 'shopify app dev' in content
|
|
222
|
+
|
|
223
|
+
@patch('builtins.input')
|
|
224
|
+
@patch('builtins.print')
|
|
225
|
+
def test_init_app(self, mock_print, mock_input, initializer, tmp_path, monkeypatch):
|
|
226
|
+
"""Test app initialization."""
|
|
227
|
+
monkeypatch.chdir(tmp_path)
|
|
228
|
+
|
|
229
|
+
# Mock user inputs
|
|
230
|
+
mock_input.side_effect = ['my-app', 'read_products,write_products']
|
|
231
|
+
|
|
232
|
+
initializer.init_app()
|
|
233
|
+
|
|
234
|
+
# Check directory created
|
|
235
|
+
app_dir = tmp_path / "my-app"
|
|
236
|
+
assert app_dir.exists()
|
|
237
|
+
|
|
238
|
+
# Check files created
|
|
239
|
+
assert (app_dir / "shopify.app.toml").exists()
|
|
240
|
+
assert (app_dir / "README.md").exists()
|
|
241
|
+
assert (app_dir / "package.json").exists()
|
|
242
|
+
|
|
243
|
+
# Check package.json content
|
|
244
|
+
package_json = json.loads((app_dir / "package.json").read_text())
|
|
245
|
+
assert package_json['name'] == 'my-app'
|
|
246
|
+
assert 'dev' in package_json['scripts']
|
|
247
|
+
|
|
248
|
+
@patch('builtins.input')
|
|
249
|
+
@patch('builtins.print')
|
|
250
|
+
def test_init_extension(self, mock_print, mock_input, initializer, tmp_path, monkeypatch):
|
|
251
|
+
"""Test extension initialization."""
|
|
252
|
+
monkeypatch.chdir(tmp_path)
|
|
253
|
+
|
|
254
|
+
# Mock user inputs: type selection (1 = checkout), name
|
|
255
|
+
mock_input.side_effect = ['1', 'my-extension']
|
|
256
|
+
|
|
257
|
+
initializer.init_extension()
|
|
258
|
+
|
|
259
|
+
# Check directory and files created
|
|
260
|
+
ext_dir = tmp_path / "my-extension"
|
|
261
|
+
assert ext_dir.exists()
|
|
262
|
+
assert (ext_dir / "shopify.extension.toml").exists()
|
|
263
|
+
assert (ext_dir / "README.md").exists()
|
|
264
|
+
|
|
265
|
+
@patch('builtins.input')
|
|
266
|
+
@patch('builtins.print')
|
|
267
|
+
def test_init_theme(self, mock_print, mock_input, initializer):
|
|
268
|
+
"""Test theme initialization."""
|
|
269
|
+
mock_input.return_value = 'my-theme'
|
|
270
|
+
|
|
271
|
+
initializer.init_theme()
|
|
272
|
+
|
|
273
|
+
assert mock_print.called
|
|
274
|
+
|
|
275
|
+
@patch('builtins.print')
|
|
276
|
+
def test_run_no_cli(self, mock_print, initializer):
|
|
277
|
+
"""Test run when CLI not installed."""
|
|
278
|
+
with patch.object(initializer, 'check_cli_installed', return_value=False):
|
|
279
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
280
|
+
initializer.run()
|
|
281
|
+
assert exc_info.value.code == 1
|
|
282
|
+
|
|
283
|
+
@patch.object(ShopifyInitializer, 'check_cli_installed', return_value=True)
|
|
284
|
+
@patch.object(ShopifyInitializer, 'init_app')
|
|
285
|
+
@patch('builtins.input')
|
|
286
|
+
@patch('builtins.print')
|
|
287
|
+
def test_run_app_selected(self, mock_print, mock_input, mock_init_app, mock_cli_check, initializer):
|
|
288
|
+
"""Test run with app selection."""
|
|
289
|
+
mock_input.return_value = '1' # Select app
|
|
290
|
+
|
|
291
|
+
initializer.run()
|
|
292
|
+
|
|
293
|
+
mock_init_app.assert_called_once()
|
|
294
|
+
|
|
295
|
+
@patch.object(ShopifyInitializer, 'check_cli_installed', return_value=True)
|
|
296
|
+
@patch.object(ShopifyInitializer, 'init_extension')
|
|
297
|
+
@patch('builtins.input')
|
|
298
|
+
@patch('builtins.print')
|
|
299
|
+
def test_run_extension_selected(self, mock_print, mock_input, mock_init_ext, mock_cli_check, initializer):
|
|
300
|
+
"""Test run with extension selection."""
|
|
301
|
+
mock_input.return_value = '2' # Select extension
|
|
302
|
+
|
|
303
|
+
initializer.run()
|
|
304
|
+
|
|
305
|
+
mock_init_ext.assert_called_once()
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class TestMain:
|
|
309
|
+
"""Test main function."""
|
|
310
|
+
|
|
311
|
+
@patch('shopify_init.ShopifyInitializer')
|
|
312
|
+
@patch('shopify_init.EnvLoader')
|
|
313
|
+
def test_main_success(self, mock_loader, mock_initializer):
|
|
314
|
+
"""Test main function success path."""
|
|
315
|
+
from shopify_init import main
|
|
316
|
+
|
|
317
|
+
mock_config = Mock()
|
|
318
|
+
mock_loader.load_config.return_value = mock_config
|
|
319
|
+
|
|
320
|
+
mock_init_instance = Mock()
|
|
321
|
+
mock_initializer.return_value = mock_init_instance
|
|
322
|
+
|
|
323
|
+
with patch('builtins.print'):
|
|
324
|
+
main()
|
|
325
|
+
|
|
326
|
+
mock_init_instance.run.assert_called_once()
|
|
327
|
+
|
|
328
|
+
@patch('shopify_init.ShopifyInitializer')
|
|
329
|
+
@patch('sys.exit')
|
|
330
|
+
def test_main_keyboard_interrupt(self, mock_exit, mock_initializer):
|
|
331
|
+
"""Test main function with keyboard interrupt."""
|
|
332
|
+
from shopify_init import main
|
|
333
|
+
|
|
334
|
+
mock_initializer.return_value.run.side_effect = KeyboardInterrupt
|
|
335
|
+
|
|
336
|
+
with patch('builtins.print'):
|
|
337
|
+
main()
|
|
338
|
+
|
|
339
|
+
mock_exit.assert_called_with(0)
|
|
340
|
+
|
|
341
|
+
@patch('shopify_init.ShopifyInitializer')
|
|
342
|
+
@patch('sys.exit')
|
|
343
|
+
def test_main_exception(self, mock_exit, mock_initializer):
|
|
344
|
+
"""Test main function with exception."""
|
|
345
|
+
from shopify_init import main
|
|
346
|
+
|
|
347
|
+
mock_initializer.return_value.run.side_effect = Exception("Test error")
|
|
348
|
+
|
|
349
|
+
with patch('builtins.print'):
|
|
350
|
+
main()
|
|
351
|
+
|
|
352
|
+
mock_exit.assert_called_with(1)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class TestEnvConfig:
|
|
356
|
+
"""Test EnvConfig dataclass."""
|
|
357
|
+
|
|
358
|
+
def test_env_config_defaults(self):
|
|
359
|
+
"""Test EnvConfig default values."""
|
|
360
|
+
config = EnvConfig()
|
|
361
|
+
|
|
362
|
+
assert config.shopify_api_key is None
|
|
363
|
+
assert config.shopify_api_secret is None
|
|
364
|
+
assert config.shop_domain is None
|
|
365
|
+
assert config.scopes is None
|
|
366
|
+
|
|
367
|
+
def test_env_config_with_values(self):
|
|
368
|
+
"""Test EnvConfig with values."""
|
|
369
|
+
config = EnvConfig(
|
|
370
|
+
shopify_api_key="key",
|
|
371
|
+
shopify_api_secret="secret",
|
|
372
|
+
shop_domain="test.myshopify.com",
|
|
373
|
+
scopes="read_products"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
assert config.shopify_api_key == "key"
|
|
377
|
+
assert config.shopify_api_secret == "secret"
|
|
378
|
+
assert config.shop_domain == "test.myshopify.com"
|
|
379
|
+
assert config.scopes == "read_products"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Shopify Agentic Starter App",
|
|
3
|
+
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-18-bullseye",
|
|
4
|
+
"forwardPorts": [
|
|
5
|
+
3000,
|
|
6
|
+
9229
|
|
7
|
+
],
|
|
8
|
+
"features": {
|
|
9
|
+
"ghcr.io/devcontainers/features/sqlite:1": {},
|
|
10
|
+
"ghcr.io/devcontainers/features/github-cli:1": {}
|
|
11
|
+
},
|
|
12
|
+
"customizations": {
|
|
13
|
+
"vscode": {
|
|
14
|
+
"settings": {
|
|
15
|
+
"editor.formatOnSave": true,
|
|
16
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
17
|
+
},
|
|
18
|
+
"extensions": [
|
|
19
|
+
"dbaeumer.vscode-eslint",
|
|
20
|
+
"esbenp.prettier-vscode",
|
|
21
|
+
"Shopify.theme-check-vscode",
|
|
22
|
+
"Shopify.ruby-lsp"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"postCreateCommand": "npm install"
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
testDir: './tests/e2e',
|
|
5
|
+
fullyParallel: true,
|
|
6
|
+
forbidOnly: !!process.env.CI,
|
|
7
|
+
retries: process.env.CI ? 2 : 0,
|
|
8
|
+
workers: process.env.CI ? 1 : undefined,
|
|
9
|
+
reporter: 'html',
|
|
10
|
+
use: {
|
|
11
|
+
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
12
|
+
// baseURL: 'http://127.0.0.1:3000',
|
|
13
|
+
trace: 'on-first-retry',
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
projects: [
|
|
17
|
+
{
|
|
18
|
+
name: 'chromium',
|
|
19
|
+
use: { ...devices['Desktop Chrome'] },
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'webkit',
|
|
23
|
+
use: { ...devices['Desktop Safari'] },
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
});
|