website
git clone https://git.pyrossh.dev/website
木 Personal website of pyrossh. Built with astrojs, shiki, vite.
7bf85c2
— pyrossh
2025-05-03T14:06:38+05:30
add playwright
Generated files not shown: bun.lock, package-lock.json
- .gitignore +6 -0
- package.json +4 -0
- playwright.config.ts +25 -0
- public/robots.txt +0 -4
- src/layouts/Base.astro +1 -1
- src/pages/cv/index.astro +279 -222
- src/pages/posts/index.astro +1 -1
- src/pages/robots.txt.ts +13 -0
- src/pages/rss.xml.js +0 -16
- src/pages/rss.xml.ts +17 -0
- tests/{test.js → home.spec.ts} +2 -2
- tests/sitemap.spec.ts +27 -0
- tests/sitemap.test.js +0 -27
.gitignore
CHANGED
|
@@ -22,3 +22,9 @@ pnpm-debug.log*
|
|
|
22
22
|
|
|
23
23
|
# jetbrains setting folder
|
|
24
24
|
.idea/
|
|
25
|
+
|
|
26
|
+
# Playwright
|
|
27
|
+
/test-results/
|
|
28
|
+
/playwright-report/
|
|
29
|
+
/blob-report/
|
|
30
|
+
/playwright/.cache/
|
package.json
CHANGED
|
@@ -15,5 +15,9 @@
|
|
|
15
15
|
"@tailwindcss/vite": "^4.1.4",
|
|
16
16
|
"astro": "^5.7.5",
|
|
17
17
|
"tailwindcss": "^4.1.4"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@playwright/test": "^1.52.0",
|
|
21
|
+
"@types/node": "^22.15.3"
|
|
18
22
|
}
|
|
19
23
|
}
|
playwright.config.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
testDir: './tests',
|
|
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
|
+
baseURL: 'http://localhost:4321',
|
|
12
|
+
trace: 'on-first-retry',
|
|
13
|
+
},
|
|
14
|
+
projects: [
|
|
15
|
+
{
|
|
16
|
+
name: 'chromium',
|
|
17
|
+
use: { ...devices['Desktop Chrome'] },
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
webServer: {
|
|
21
|
+
command: 'bun run dev',
|
|
22
|
+
url: 'http://localhost:4321',
|
|
23
|
+
reuseExistingServer: !process.env.CI,
|
|
24
|
+
},
|
|
25
|
+
});
|
public/robots.txt
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
User-agent: *
|
|
2
|
-
Allow: /
|
|
3
|
-
|
|
4
|
-
Sitemap: https://pyrossh.dev/sitemap.xml
|
src/layouts/Base.astro
CHANGED
|
@@ -14,7 +14,7 @@ interface Props {
|
|
|
14
14
|
|
|
15
15
|
const { title, description } = Astro.props;
|
|
16
16
|
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
|
17
|
-
const image = "/
|
|
17
|
+
const image = "/favicon.png";
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
<!doctype html>
|
src/pages/cv/index.astro
CHANGED
|
@@ -1,235 +1,292 @@
|
|
|
1
1
|
---
|
|
2
|
-
import Layout from
|
|
2
|
+
import Layout from "@/layouts/Base.astro";
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
<Layout title="
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
5
|
+
<Layout title="cv" description="Peter John's CV">
|
|
6
|
+
<div>
|
|
7
|
+
<section>
|
|
8
|
+
<h3>Equal Experts</h3>
|
|
9
|
+
<h4>Software Developer, Oct 2018 - present</h4>
|
|
10
|
+
<h5>
|
|
11
|
+
Making Software. Better. Equal Experts is a network of talented,
|
|
12
|
+
experienced software consultants, specialising in agile delivery.
|
|
13
|
+
</h5>
|
|
14
|
+
<div>
|
|
15
|
+
These are the client projects I worked on,
|
|
16
|
+
<ul>
|
|
17
|
+
<li>
|
|
18
|
+
<h6>John Lewis <span>(Mar '21 - Aug '24)</span></h6>
|
|
19
|
+
<ul class="*:list-disc">
|
|
20
|
+
<li>
|
|
21
|
+
Built a quiz memory game with leaderboard scoring to improve
|
|
22
|
+
engagement during Christmas
|
|
23
|
+
</li>
|
|
24
|
+
<li>
|
|
25
|
+
Implemented new features for customer self-serve actions in
|
|
26
|
+
returns, cancellations, tracking
|
|
27
|
+
</li>
|
|
28
|
+
<li>
|
|
29
|
+
Refactored the old self-serve React app to use Nextjs and
|
|
30
|
+
removed a large dependency on redux
|
|
31
|
+
</li>
|
|
32
|
+
<li>
|
|
33
|
+
Ran many experiments in the customer services and order pages to
|
|
34
|
+
improve our metrics and reduce contacts
|
|
35
|
+
</li>
|
|
36
|
+
<li>
|
|
37
|
+
Built the new Product Description Page (PDP) for Simple Carpets
|
|
38
|
+
using a micro frontend architecture
|
|
39
|
+
</li>
|
|
40
|
+
<li>
|
|
41
|
+
<b>Technologies & Tools</b>: Nodejs, Nextjs, Reactjs, Kotlin,
|
|
42
|
+
K8s, Grafana, Kibana, Firestore, Google Sheets API, Monetate,
|
|
43
|
+
Adobe Analytics, Scene7, Adobe AEM
|
|
44
|
+
</li>
|
|
45
|
+
</ul>
|
|
46
|
+
</li>
|
|
47
|
+
<li>
|
|
48
|
+
<h6>iOWNA <span>(Mar '20 - Mar '21)</span></h6>
|
|
49
|
+
<ul class="*:list-disc">
|
|
50
|
+
<li>
|
|
51
|
+
Built a Content Editor for curators to create content on our
|
|
52
|
+
platform
|
|
53
|
+
</li>
|
|
54
|
+
<li>
|
|
55
|
+
Built the iOWNA crossplatform app in react-native that works in
|
|
56
|
+
android, ios, and web using one codebase.
|
|
57
|
+
</li>
|
|
58
|
+
<li>Implemented Admin specific functionalities in the web app</li>
|
|
59
|
+
<li>
|
|
60
|
+
Implemented an in-app OTA update method using expo and semantic
|
|
61
|
+
versioning.
|
|
62
|
+
</li>
|
|
63
|
+
<li>Built the iOWNA marketing website</li>
|
|
64
|
+
<li>
|
|
65
|
+
Built the Health Questionnaire clinician/patient feedback form
|
|
66
|
+
feature with push notifications
|
|
67
|
+
</li>
|
|
68
|
+
<li>
|
|
69
|
+
<b>Technologies & Tools</b>: Go, Javascript, React Native, Expo,
|
|
70
|
+
GraphQL, Serverless, AppSync, Cognito, Dynamodb, API Gateway,
|
|
71
|
+
Lambda, Cloudformation
|
|
72
|
+
</li>
|
|
73
|
+
</ul>
|
|
74
|
+
</li>
|
|
75
|
+
<li>
|
|
76
|
+
<h6>Zeta <span>(Nov '19 - Feb '20)</span></h6>
|
|
77
|
+
<ul class="*:list-disc">
|
|
78
|
+
<li>
|
|
79
|
+
Added multiple download formats (pdf,html,xlsx) for reports in
|
|
80
|
+
the Reporting Center
|
|
81
|
+
</li>
|
|
82
|
+
<li>
|
|
83
|
+
Created a Groovy Script to automate creating ACL in sandbox
|
|
84
|
+
authorization framework
|
|
85
|
+
</li>
|
|
86
|
+
<li>
|
|
87
|
+
Integrated Camunda Workflow Engine into Zeta services in
|
|
88
|
+
Operations Center
|
|
89
|
+
</li>
|
|
90
|
+
<li>
|
|
91
|
+
Built a few workflows on Camunda and integrated it with some
|
|
92
|
+
internal services
|
|
93
|
+
</li>
|
|
94
|
+
<li>
|
|
95
|
+
Built a custom k8s CRD using the operator framework to automate
|
|
96
|
+
creating ACL in sandbox using custom resources
|
|
97
|
+
</li>
|
|
98
|
+
<li>
|
|
99
|
+
<b>Technologies & Tools</b>: Go, Java, Springboot, Camunda,
|
|
100
|
+
Postgres, Docker, K8s
|
|
101
|
+
</li>
|
|
102
|
+
</ul>
|
|
103
|
+
</li>
|
|
104
|
+
<li>
|
|
105
|
+
<h6>Lifebox <span>(Oct '18 - Oct '19)</span></h6>
|
|
106
|
+
<ul class="*:list-disc">
|
|
107
|
+
<li>
|
|
108
|
+
Added search functionality to the hospital procedure codes
|
|
109
|
+
</li>
|
|
110
|
+
<li>
|
|
111
|
+
Converted the Health Questionnaire from json to native go code
|
|
112
|
+
making it easier to make changes to it
|
|
113
|
+
</li>
|
|
114
|
+
<li>
|
|
115
|
+
Integrated with SAP using HL7 message format to create patients,
|
|
116
|
+
episodes, schedule/cancel procedures
|
|
117
|
+
</li>
|
|
118
|
+
<li>
|
|
119
|
+
Built the Patient Document repository for uploading, downloading
|
|
120
|
+
patient files
|
|
121
|
+
</li>
|
|
122
|
+
<li>
|
|
123
|
+
Built the patients online reports view using the data from SAP
|
|
124
|
+
</li>
|
|
125
|
+
<li>
|
|
126
|
+
<b>Technologies & Tools</b>: Go, Javascript, Reactjs, K6,
|
|
127
|
+
Serverless, Grafana, Cognito, Dynamodb, API Gateway , Lambda,
|
|
128
|
+
GraphQL, Cloudformation, SQLite3
|
|
129
|
+
</li>
|
|
130
|
+
</ul>
|
|
131
|
+
</li>
|
|
132
|
+
</ul>
|
|
133
|
+
</div>
|
|
134
|
+
</section>
|
|
135
|
+
<section>
|
|
136
|
+
<h3>Numberz</h3>
|
|
137
|
+
<h4>Full Stack Developer, Sept 2016 - Oct 2018</h4>
|
|
138
|
+
<h5>
|
|
139
|
+
Numberz integrates banking with your day-2-day business work-flows
|
|
140
|
+
freeing up a lot of effort, time and heart-burn, to help your business
|
|
141
|
+
grow.
|
|
142
|
+
</h5>
|
|
143
|
+
<ul class="*:list-disc">
|
|
144
|
+
<li>
|
|
145
|
+
Implemented new features and fixed bugs on the numberz android app
|
|
146
|
+
</li>
|
|
147
|
+
<li>
|
|
148
|
+
Integrated numberz with Tally ERP (export and import of sales,
|
|
149
|
+
purchases)
|
|
150
|
+
</li>
|
|
151
|
+
<li>
|
|
152
|
+
Implemented CI and Devops within out system to ease our deployment
|
|
153
|
+
process using docker and docker-compose
|
|
154
|
+
</li>
|
|
155
|
+
<li>
|
|
156
|
+
Created a slack bot called deploybot which was used to deploy our
|
|
157
|
+
microservices to our servers
|
|
158
|
+
</li>
|
|
159
|
+
<li>
|
|
160
|
+
Started tech talks on Thursdays to discuss cutting edge technologies
|
|
161
|
+
</li>
|
|
162
|
+
<li>
|
|
163
|
+
Architected and built the Notification Service using rabbitmq and
|
|
164
|
+
nodejs
|
|
165
|
+
</li>
|
|
166
|
+
<li>Built the Multi-User and Multi-Company Features</li>
|
|
167
|
+
<li>
|
|
168
|
+
Implemented and lead a team on integration with the GSTN System so
|
|
169
|
+
that customers could file their GST Taxes within our product
|
|
170
|
+
</li>
|
|
171
|
+
<li>
|
|
172
|
+
Architected and built the numberz Identity Management Service(IMS)
|
|
173
|
+
</li>
|
|
174
|
+
<li>
|
|
175
|
+
Architected our new product called the Numberz Accounts Receivable
|
|
176
|
+
(AR)
|
|
177
|
+
</li>
|
|
178
|
+
<li>Laid down the architecture for the frontend and backend</li>
|
|
179
|
+
<li>
|
|
180
|
+
Built these core features for our new AR Product - Auto Reminders,
|
|
181
|
+
Manual Reminders, User Management, Cash Discounting, CFO Dashboard
|
|
182
|
+
Metrics
|
|
183
|
+
</li>
|
|
184
|
+
<li>
|
|
185
|
+
<b>Technologies & Tools</b>: Nodejs, Mongodb, Postgres, GraphQL,
|
|
186
|
+
Ansible, K8s, Typescript, Reactjs
|
|
187
|
+
</li>
|
|
188
|
+
</ul>
|
|
189
|
+
</section>
|
|
190
|
+
<section>
|
|
191
|
+
<h3>Playlyfe</h3>
|
|
192
|
+
<h4>Full Stack Developer, Apr 2014 - Sept 2016</h4>
|
|
193
|
+
<h5>
|
|
194
|
+
Playlyfe is an online Gamification Platform which empowers anyone to
|
|
195
|
+
design and implement a gamified system.
|
|
196
|
+
</h5>
|
|
197
|
+
<ul class="*:list-disc">
|
|
198
|
+
<li>
|
|
199
|
+
Built the Playlyfe Hybrid Mobile app using Phonegap for Android and
|
|
200
|
+
iOS
|
|
201
|
+
</li>
|
|
202
|
+
<li>
|
|
203
|
+
Built the leaderboard system using Mongodb, Redis and Couchbase as the
|
|
204
|
+
datastore
|
|
205
|
+
</li>
|
|
206
|
+
<li>Migration of data from v1 to v2 of the API</li>
|
|
207
|
+
<li>
|
|
208
|
+
Built SDKs for the Playlyfe v2 API in C#, Java, Python, Ruby, PHP,
|
|
209
|
+
Nodejs, Gos
|
|
210
|
+
</li>
|
|
211
|
+
<li>Maintained the job server and wrote most of the background jobs</li>
|
|
212
|
+
<li>
|
|
213
|
+
Added documentation to most of the REST API and created the Developer
|
|
214
|
+
Console which was Like Swagger UI
|
|
215
|
+
</li>
|
|
216
|
+
<li>Integrated our REST API in Leanosphere's LMS</li>
|
|
217
|
+
<li>Integrated the REST API in Knolskape's product Aktivlearn LMS</li>
|
|
218
|
+
<li>Integrated the REST API in Linkstreet's LMS</li>
|
|
219
|
+
<li>
|
|
220
|
+
Integrated the REST API in Moodle LMS by creating an admin plugin to
|
|
221
|
+
add Gamification features
|
|
222
|
+
</li>
|
|
223
|
+
<li>
|
|
224
|
+
Rebuilt the core platform in golang for performance and developement
|
|
225
|
+
speed
|
|
226
|
+
</li>
|
|
227
|
+
<li>Built the GraphQL API for our cutting edge product Catalyst</li>
|
|
228
|
+
<li>
|
|
229
|
+
Convinced the boss to create our own scripting language plscript for
|
|
230
|
+
our rule engine.
|
|
231
|
+
</li>
|
|
232
|
+
<li>
|
|
233
|
+
Build a REST API for our product catalyst for a POC with Vodafone
|
|
234
|
+
</li>
|
|
235
|
+
<li>
|
|
236
|
+
Built the Quiz, Review, Match, Tournament features in Catalyst for a
|
|
237
|
+
major project with HP
|
|
238
|
+
</li>
|
|
239
|
+
<li>
|
|
240
|
+
Added gamification features to Saudi Matches a popular game tracking
|
|
241
|
+
app
|
|
242
|
+
</li>
|
|
243
|
+
<li>
|
|
244
|
+
<b>Technologies & Tools</b>: Nodejs, Coffeescript, Angularjs, Mongodb,
|
|
245
|
+
Couchbase, Redis, Seaweedfs, Postgres, Golang, GraphQL, Relay, React
|
|
246
|
+
</li>
|
|
247
|
+
</ul>
|
|
248
|
+
</section>
|
|
249
|
+
</div>
|
|
193
250
|
</Layout>
|
|
194
251
|
<style>
|
|
195
|
-
|
|
252
|
+
b {
|
|
196
|
-
|
|
253
|
+
font-weight: 600;
|
|
197
|
-
|
|
254
|
+
}
|
|
198
255
|
|
|
199
|
-
|
|
256
|
+
ul {
|
|
200
|
-
|
|
257
|
+
margin: 0.5rem;
|
|
201
|
-
|
|
258
|
+
padding-left: 1rem;
|
|
202
|
-
|
|
259
|
+
}
|
|
203
260
|
|
|
204
|
-
|
|
261
|
+
li {
|
|
205
|
-
|
|
262
|
+
list-style-type: disc;
|
|
206
|
-
|
|
263
|
+
list-style-position: outside;
|
|
207
|
-
|
|
264
|
+
}
|
|
208
265
|
|
|
209
|
-
|
|
266
|
+
h3 {
|
|
210
|
-
|
|
267
|
+
font-weight: 700;
|
|
211
|
-
|
|
268
|
+
font-size: 1.4rem;
|
|
212
|
-
|
|
269
|
+
margin-top: 0.5rem;
|
|
213
|
-
|
|
270
|
+
margin-bottom: 0.5rem;
|
|
214
|
-
|
|
271
|
+
}
|
|
215
272
|
|
|
216
|
-
|
|
273
|
+
h4 {
|
|
217
|
-
|
|
274
|
+
font-weight: 600;
|
|
218
|
-
|
|
275
|
+
margin-top: 0.5rem;
|
|
219
|
-
|
|
276
|
+
}
|
|
220
277
|
|
|
221
|
-
|
|
278
|
+
h5 {
|
|
222
|
-
|
|
279
|
+
margin-top: 0.5rem;
|
|
223
|
-
|
|
280
|
+
}
|
|
224
281
|
|
|
225
|
-
|
|
282
|
+
h6 {
|
|
226
|
-
|
|
283
|
+
font-weight: 600;
|
|
227
|
-
|
|
284
|
+
margin-top: 0.5rem;
|
|
228
285
|
|
|
229
|
-
|
|
286
|
+
span {
|
|
230
|
-
|
|
287
|
+
font-family: sans-serif;
|
|
231
|
-
|
|
288
|
+
font-weight: 400;
|
|
232
|
-
|
|
289
|
+
font-size: 0.95rem;
|
|
233
|
-
|
|
290
|
+
}
|
|
234
|
-
|
|
291
|
+
}
|
|
235
292
|
</style>
|
src/pages/posts/index.astro
CHANGED
|
@@ -8,7 +8,7 @@ const posts = (await getCollection("content")).sort(
|
|
|
8
8
|
);
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
<Layout>
|
|
11
|
+
<Layout title="Posts" description="Posts">
|
|
12
12
|
<h1 class="font-bold text-3xl">Posts</h1>
|
|
13
13
|
<div class="container">
|
|
14
14
|
<ul class="flex flex-col">
|
src/pages/robots.txt.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
|
|
3
|
+
const getRobotsTxt = (sitemapURL: URL) =>
|
|
4
|
+
`User-agent: *
|
|
5
|
+
Allow: /
|
|
6
|
+
|
|
7
|
+
Sitemap: ${sitemapURL.href}
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
export const GET: APIRoute = ({ site }) => {
|
|
11
|
+
const sitemapURL = new URL('sitemap-index.xml', site);
|
|
12
|
+
return new Response(getRobotsTxt(sitemapURL));
|
|
13
|
+
};
|
src/pages/rss.xml.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import rss from '@astrojs/rss';
|
|
2
|
-
import { getCollection } from 'astro:content';
|
|
3
|
-
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
|
|
4
|
-
|
|
5
|
-
export async function GET(context) {
|
|
6
|
-
const posts = await getCollection('blog');
|
|
7
|
-
return rss({
|
|
8
|
-
title: SITE_TITLE,
|
|
9
|
-
description: SITE_DESCRIPTION,
|
|
10
|
-
site: context.site,
|
|
11
|
-
items: posts.map((post) => ({
|
|
12
|
-
...post.data,
|
|
13
|
-
link: `/blog/${post.id}/`,
|
|
14
|
-
})),
|
|
15
|
-
});
|
|
16
|
-
}
|
src/pages/rss.xml.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
import { getCollection } from 'astro:content';
|
|
3
|
+
import rss from '@astrojs/rss';
|
|
4
|
+
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
|
|
5
|
+
|
|
6
|
+
export const GET: APIRoute = async ({ site }) => {
|
|
7
|
+
const posts = await getCollection('blog');
|
|
8
|
+
return rss({
|
|
9
|
+
title: SITE_TITLE,
|
|
10
|
+
description: SITE_DESCRIPTION,
|
|
11
|
+
site: site,
|
|
12
|
+
items: posts.map((post) => ({
|
|
13
|
+
...post.data,
|
|
14
|
+
link: `/posts/${post.id}/`,
|
|
15
|
+
})),
|
|
16
|
+
});
|
|
17
|
+
}
|
tests/{test.js → home.spec.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expect, test } from '@playwright/test';
|
|
2
2
|
|
|
3
3
|
test('index page has expected h1', async ({ page }) => {
|
|
4
|
-
|
|
4
|
+
await page.goto('/');
|
|
5
|
-
|
|
5
|
+
await expect(page.getByRole('heading', { name: 'Hello!' })).toBeVisible();
|
|
6
6
|
});
|
tests/sitemap.spec.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
test('/sitemap.xml is valid', async ({ page }) => {
|
|
4
|
+
const response = await page.goto('/sitemap.xml');
|
|
5
|
+
expect(response.status()).toBe(200);
|
|
6
|
+
|
|
7
|
+
// Ensure XML is valid. Playwright parses the XML here and will error if it
|
|
8
|
+
// cannot be parsed.
|
|
9
|
+
const urls = await page.$$eval('url', (urls) =>
|
|
10
|
+
urls.map((url) => ({
|
|
11
|
+
loc: url.querySelector('loc').textContent,
|
|
12
|
+
// changefreq: url.querySelector('changefreq').textContent, // if you enabled in your sitemap
|
|
13
|
+
// priority: url.querySelector('priority').textContent,
|
|
14
|
+
})),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// Sanity check
|
|
18
|
+
expect(urls.length).toBeGreaterThanOrEqual(5);
|
|
19
|
+
|
|
20
|
+
// Ensure entries are in a valid format.
|
|
21
|
+
for (const url of urls) {
|
|
22
|
+
expect(url.loc).toBeTruthy();
|
|
23
|
+
expect(() => new URL(url.loc)).not.toThrow();
|
|
24
|
+
// expect(url.changefreq).toBe('daily');
|
|
25
|
+
// expect(url.priority).toBe('0.7');
|
|
26
|
+
}
|
|
27
|
+
});
|
tests/sitemap.test.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { expect, test } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
test('/sitemap.xml is valid', async ({ page }) => {
|
|
4
|
-
const response = await page.goto('/sitemap.xml');
|
|
5
|
-
expect(response.status()).toBe(200);
|
|
6
|
-
|
|
7
|
-
// Ensure XML is valid. Playwright parses the XML here and will error if it
|
|
8
|
-
// cannot be parsed.
|
|
9
|
-
const urls = await page.$$eval('url', (urls) =>
|
|
10
|
-
urls.map((url) => ({
|
|
11
|
-
loc: url.querySelector('loc').textContent,
|
|
12
|
-
// changefreq: url.querySelector('changefreq').textContent, // if you enabled in your sitemap
|
|
13
|
-
// priority: url.querySelector('priority').textContent,
|
|
14
|
-
})),
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
// Sanity check
|
|
18
|
-
expect(urls.length).toBeGreaterThanOrEqual(5);
|
|
19
|
-
|
|
20
|
-
// Ensure entries are in a valid format.
|
|
21
|
-
for (const url of urls) {
|
|
22
|
-
expect(url.loc).toBeTruthy();
|
|
23
|
-
expect(() => new URL(url.loc)).not.toThrow();
|
|
24
|
-
// expect(url.changefreq).toBe('daily');
|
|
25
|
-
// expect(url.priority).toBe('0.7');
|
|
26
|
-
}
|
|
27
|
-
});
|