@@ -247,7 +247,7 @@ describe('lint markdown content', () => {
247247 describe . each ( mdToLint ) (
248248 '%s' ,
249249 ( markdownRelPath , markdownAbsPath ) => {
250- let content , ast , links , isHidden , isEarlyAccess , isSitePolicy , frontmatterErrors , frontmatterData
250+ let content , ast , links , yamlScheduledWorkflows , isHidden , isEarlyAccess , isSitePolicy , frontmatterErrors , frontmatterData
251251
252252 beforeAll ( async ( ) => {
253253 const fileContents = await readFileAsync ( markdownAbsPath , 'utf8' )
@@ -265,6 +265,23 @@ describe('lint markdown content', () => {
265265 visit ( ast , [ 'link' , 'definition' ] , node => {
266266 links . push ( node . url )
267267 } )
268+
269+ yamlScheduledWorkflows = [ ]
270+ visit ( ast , 'code' , node => {
271+ if ( node . lang === 'yaml' && node . value . includes ( 'schedule' ) && node . value . includes ( 'cron' ) ) {
272+ yamlScheduledWorkflows . push ( node . value )
273+ }
274+ } )
275+
276+ // visit is not async-friendly so we need to do an async map to parse the YML snippets
277+ yamlScheduledWorkflows = ( await Promise . all ( yamlScheduledWorkflows . map ( async ( snippet ) => {
278+ // If we don't parse the Liquid first, yaml loading chokes on {% raw %} tags
279+ const rendered = await renderContent . liquid . parseAndRender ( snippet )
280+ const parsed = yaml . safeLoad ( rendered )
281+ return parsed . on . schedule
282+ } ) ) )
283+ . flat ( )
284+ . map ( schedule => schedule . cron )
268285 } )
269286
270287 // We need to support some non-Early Access hidden docs in Site Policy
@@ -292,6 +309,20 @@ describe('lint markdown content', () => {
292309 expect ( matches . length , errorMessage ) . toBe ( 0 )
293310 } )
294311
312+ test ( 'yaml snippets that include scheduled workflows must not run on the hour' , async ( ) => {
313+ const hourlySchedules = yamlScheduledWorkflows . filter ( schedule => {
314+ const hour = schedule . split ( ' ' ) [ 0 ]
315+ // return any minute cron segments that equal 0, 00, 000, etc.
316+ return ! / [ ^ 0 ] / . test ( hour )
317+ } )
318+ expect ( hourlySchedules ) . toEqual ( [ ] )
319+ } )
320+
321+ // Note this only ensures that scheduled workflow snippets are unique _per Markdown file_
322+ test ( 'yaml snippets that include scheduled workflows run at unique times' , ( ) => {
323+ expect ( yamlScheduledWorkflows . length ) . toEqual ( new Set ( yamlScheduledWorkflows ) . size )
324+ } )
325+
295326 test ( 'URLs must not contain a hard-coded language code' , async ( ) => {
296327 const matches = links . filter ( link => {
297328 return / \/ (?: $ { languageCodes.j o i n ( ' | ' ) } ) \/ / . test ( link )
0 commit comments