Last active
July 22, 2021 10:24
-
-
Save crutchcorn/f3fca48a0468f9df983cc99f3ea8c86e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
data.data.allMarkdownRemark.nodes.filter(md => md.frontmatter.license.id !== 'coderpad').map(md => ({ | |
title: md.frontmatter.title, | |
slug: "https://unicorn-utterances.com/posts" + md.fields.slug, | |
year: (new Date(md.frontmatter.published)).getFullYear(), | |
day: (new Date(md.frontmatter.published)).getDate(), | |
month: (new Date(md.frontmatter.published)).getMonth(), | |
})) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
query BlogListPageQuery { | |
allMarkdownRemark( | |
filter: {frontmatter: {authors: {elemMatch: {id: {eq: "crutchcorn"}}}}} | |
) { | |
nodes { | |
frontmatter { | |
title | |
license { | |
id | |
} | |
published | |
} | |
fields { | |
slug | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Start at profile page | |
// EG: https://www.polywork.com/crutchcorn/ | |
const wait = (time) => new Promise(resolve => { | |
setTimeout(() => { | |
resolve(); | |
}, time); | |
}); | |
const waitFor = async (cb, maxWait = 5000) => { | |
let remainingTime = maxWait; | |
while (remainingTime > 0) { | |
const val = cb(); | |
remainingTime -= 100; | |
if (!val) { | |
await wait(100); | |
} else { | |
return val; | |
} | |
} | |
}; | |
const waitAndClick = async (cb) => { | |
const el = await waitFor(cb); | |
el.click(); | |
}; | |
const monthArr = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; | |
const blogName = "Unicorn Utterances"; | |
for (const blogPost of blogPosts) { | |
await submitPost(blogPost); | |
} | |
async function submitPost(blogInfo) { | |
const postBtn = await waitFor(() => document.querySelector("[href$=\"/highlights/new\"]")); | |
postBtn.click(); | |
const selectEditModal = () => waitFor(() => document.querySelector("[id^=\"edit_draft_highlight\"]")); | |
let modal = await selectEditModal(); | |
// Select date | |
// ******************************************************************************************************************** | |
await (async () => { | |
const datePicker = await waitFor(() => modal.querySelector("[id=\"highlight-date-picker\"]")); | |
datePicker.click(); | |
const pickerEditBtn = await waitFor(() => modal.querySelector("[data-action=\"click->highlight-date#showDatePicker\"]")); | |
pickerEditBtn.click(); | |
// Year picker | |
await waitAndClick(() => modal.querySelector(".datepicker-switch")); | |
// Wait for calendar to show up | |
await waitFor(() => modal.querySelector(".datepicker-years .year")); | |
let yearBtn = null; | |
let attemptedYears = 0; | |
while (!yearBtn && attemptedYears < 10) { | |
yearBtn = [...modal.querySelectorAll(".year:not(.disabled)")].find(year => year.innerText === blogInfo.year.toString()); | |
attemptedYears += 1; | |
if (!yearBtn) { | |
await waitAndClick(() => modal.querySelector(".prev")); | |
} | |
} | |
yearBtn.click(); | |
let monthBtn = null; | |
let attemptedMonths = 0; | |
while (!monthBtn && attemptedMonths < 10) { | |
const monthName = monthArr[blogInfo.month]; | |
monthBtn = [...modal.querySelectorAll(".month:not(.disabled)")].find(month => month.innerText === monthName); | |
attemptedMonths += 1; | |
if (!monthBtn) { | |
await waitAndClick(() => modal.querySelector(".prev")); | |
} | |
} | |
monthBtn.click(); | |
let dayBtn = null; | |
let attemptedDays = 0; | |
while (!dayBtn && attemptedDays < 10) { | |
dayBtn = [...modal.querySelectorAll(".day:not(.new):not(.old)")].find(day => day.innerText === blogInfo.day.toString()); | |
attemptedDays += 1; | |
if (!dayBtn) { | |
await waitAndClick(() => modal.querySelector(".prev")); | |
} | |
} | |
dayBtn.click(); | |
})(); | |
// ******************************************************************************************************************** | |
// Set editor value | |
// ******************************************************************************************************************** | |
const editor = await waitFor(() => modal.querySelector("[id=\"draft_highlight_content\"]")); | |
editor.value = ` | |
I wrote an article! | |
${blogInfo.title} | |
${blogInfo.slug} | |
`.trim().replace(/\n/g, "<br>"); | |
// ******************************************************************************************************************** | |
// Set position | |
// ******************************************************************************************************************** | |
await (async () => { | |
await waitAndClick(() => modal.querySelector("[formaction$=\"highlight_position\"]")); | |
modal = await waitFor(() => document.querySelector("[id=\"highlight_position_modal\"]")); | |
// Opens a new modal. Must reload old modal after selection | |
const positions = await waitFor(() => { | |
const els = modal.querySelectorAll("[for^=\"highlight-position\"]"); | |
return els.length ? [...els] : null; | |
}); | |
positions.find(e => e.innerText.includes(blogName)).click(); | |
await waitAndClick(() => modal.querySelector("[type=\"submit\"]")); | |
modal = await selectEditModal(); | |
})(); | |
// ******************************************************************************************************************** | |
// Set tags | |
// ******************************************************************************************************************** | |
await (async () => { | |
const collapseSuggestionsBtn = await waitFor(() => modal.querySelector("[href=\"#collapseActivitySuggestions\"]")); | |
if (collapseSuggestionsBtn.ariaExpanded === "true") { | |
collapseSuggestionsBtn.click(); | |
} | |
await waitAndClick(() => modal.querySelector("[formaction$=\"highlight_activities\"]")); | |
// Opens a new modal. Must reload old modal after selection | |
modal = await waitFor(() => document.querySelector("[id=\"highlight_activity_search_modal\"]")); | |
const findTagAndSelect = async (tagName) => { | |
const activitySearch = await waitFor(() => { | |
const formEl = modal.querySelector("[action$=\"/highlight_activities/search\"]"); | |
return formEl && formEl.querySelector("input"); | |
}); | |
activitySearch.value = tagName; | |
activitySearch.dispatchEvent(new FocusEvent("focusin")); | |
const tagList = await waitFor(() => { | |
const els = modal.querySelectorAll("[href$=\"/tag\"].list-item-wrapper"); | |
return els.length ? [...els] : null; | |
}); | |
const tag = tagList.find(el => el.innerText.replace(/\s+/g, " ") === tagName); | |
tag.click(); | |
}; | |
await findTagAndSelect("Wrote an article"); | |
await findTagAndSelect("Contributed to open source"); | |
await waitAndClick(() => modal.querySelector("[form=\"tagged_activities_form\"][type=\"submit\"]")); | |
modal = await selectEditModal(); | |
})(); | |
// ******************************************************************************************************************** | |
// Submit post | |
// ******************************************************************************************************************** | |
await waitAndClick(() => modal.querySelector("[value=\"Post\"]")); | |
await waitAndClick(() => { | |
const tmpModal = document.querySelector("#highlight_success_share_modal"); | |
const close = tmpModal && tmpModal.querySelector("[aria-label=\"Close\"]"); | |
return close; | |
}); | |
// ******************************************************************************************************************** | |
} | |
// Months are zero indexed. Should be fine | |
var blogPosts = [{ | |
"title": "Better Angular Form Components with ngModel and formControl Implementation", | |
"slug": "https://unicorn-utterances.com/posts/angular-components-control-value-accessor/", | |
"year": 2020, | |
"day": 9, | |
"month": 5 | |
}, { | |
"title": "Package Font Files on NPM for Angular Usage", | |
"slug": "https://unicorn-utterances.com/posts/angular-npm-font-usage/", | |
"year": 2020, | |
"day": 24, | |
"month": 10 | |
}, { | |
"title": "Networking 101: A Basic Overview of Packets and OSI", | |
"slug": "https://unicorn-utterances.com/posts/basic-overview-of-packets-and-osi/", | |
"year": 2020, | |
"day": 11, | |
"month": 2 | |
}, { | |
"title": "Write Simpler Tests - 5 Suggestions for Better Tests", | |
"slug": "https://unicorn-utterances.com/posts/five-suggestions-for-simpler-tests/", | |
"year": 2020, | |
"day": 25, | |
"month": 4 | |
}, { | |
"title": "Draw under the Android NavBar Using React Native", | |
"slug": "https://unicorn-utterances.com/posts/draw-under-navbar-using-react-native/", | |
"year": 2020, | |
"day": 15, | |
"month": 3 | |
}, { | |
"title": "Setup Android Studio Emulator for AMD Ryzen CPUs", | |
"slug": "https://unicorn-utterances.com/posts/android-studio-setup-for-ryzen-cpus/", | |
"year": 2020, | |
"day": 5, | |
"month": 4 | |
}, { | |
"title": "Data Storage Options for React Native", | |
"slug": "https://unicorn-utterances.com/posts/data-storage-options-in-react-native/", | |
"year": 2020, | |
"day": 13, | |
"month": 3 | |
}, { | |
"title": "Debugging NodeJS Applications Using Chrome", | |
"slug": "https://unicorn-utterances.com/posts/debugging-nodejs-programs-using-chrome/", | |
"year": 2020, | |
"day": 20, | |
"month": 0 | |
}, { | |
"title": "How to Pick Tech Stacks For New Projects", | |
"slug": "https://unicorn-utterances.com/posts/how-to-pick-tech-stacks-for-new-projects/", | |
"year": 2020, | |
"day": 1, | |
"month": 2 | |
}, { | |
"title": "WebDev 101: How to use npm and Yarn", | |
"slug": "https://unicorn-utterances.com/posts/how-to-use-npm/", | |
"year": 2021, | |
"day": 5, | |
"month": 3 | |
}, { | |
"title": "How Computers Speak: Assembly to AST", | |
"slug": "https://unicorn-utterances.com/posts/how-computers-speak/", | |
"year": 2020, | |
"day": 24, | |
"month": 7 | |
}, { | |
"title": "Integrating Native Android Code in Unity", | |
"slug": "https://unicorn-utterances.com/posts/integrating-android-code-in-unity/", | |
"year": 2020, | |
"day": 3, | |
"month": 0 | |
}, { | |
"title": "Introduction to TypeScript — What is TypeScript?", | |
"slug": "https://unicorn-utterances.com/posts/introduction-to-typescript/", | |
"year": 2019, | |
"day": 12, | |
"month": 9 | |
}, { | |
"title": "Building an Angular Blog With Scully", | |
"slug": "https://unicorn-utterances.com/posts/making-an-angular-blog-with-scully/", | |
"year": 2020, | |
"day": 16, | |
"month": 2 | |
}, { | |
"title": "Making a Slack Bot using NodeJS and MongoDB", | |
"slug": "https://unicorn-utterances.com/posts/making-a-slack-bot-with-node-and-mongo/", | |
"year": 2020, | |
"day": 17, | |
"month": 1 | |
}, { | |
"title": "Networking 101: UDP & TCP", | |
"slug": "https://unicorn-utterances.com/posts/networking-101-udp-and-tcp/", | |
"year": 2020, | |
"day": 30, | |
"month": 2 | |
}, { | |
"title": "How Binary and Hexadecimal Work: An introduction to non-decimal number systems", | |
"slug": "https://unicorn-utterances.com/posts/non-decimal-numbers-in-tech/", | |
"year": 2019, | |
"day": 6, | |
"month": 10 | |
}, { | |
"title": "React Refs: The Complete Story", | |
"slug": "https://unicorn-utterances.com/posts/react-refs-complete-story/", | |
"year": 2020, | |
"day": 30, | |
"month": 10 | |
}, { | |
"title": "Understanding The DOM: How Browsers Show Content On-Screen", | |
"slug": "https://unicorn-utterances.com/posts/understanding-the-dom/", | |
"year": 2019, | |
"day": 26, | |
"month": 10 | |
}, { | |
"title": "Autogenerate Changelogs and Manage Releases using Conventional Commit", | |
"slug": "https://unicorn-utterances.com/posts/setup-standard-version/", | |
"year": 2020, | |
"day": 22, | |
"month": 5 | |
}, { | |
"title": "TypeScript Intermediates - Type Generics", | |
"slug": "https://unicorn-utterances.com/posts/typescript-type-generics/", | |
"year": 2019, | |
"day": 25, | |
"month": 8 | |
}, { | |
"title": "Uttering Hello — The Site's First Post", | |
"slug": "https://unicorn-utterances.com/posts/uttering-hello-introduction-post/", | |
"year": 2019, | |
"day": 29, | |
"month": 5 | |
}, { | |
"title": "The Ultimate Windows Development Environment Guide", | |
"slug": "https://unicorn-utterances.com/posts/ultimate-windows-development-environment-guide/", | |
"year": 2020, | |
"day": 6, | |
"month": 3 | |
}, { | |
"title": "Adding Cathage Dependencies into React Native", | |
"slug": "https://unicorn-utterances.com/posts/using-carthage-with-react-native/", | |
"year": 2020, | |
"day": 13, | |
"month": 9 | |
}, { | |
"title": "What is Server Side Rendering (SSR) and Static Site Generation (SSG)?", | |
"slug": "https://unicorn-utterances.com/posts/what-is-ssr-and-ssg/", | |
"year": 2020, | |
"day": 23, | |
"month": 2 | |
}, { | |
"title": "Angular Templates — From Start to Source", | |
"slug": "https://unicorn-utterances.com/posts/angular-templates-start-to-source/", | |
"year": 2019, | |
"day": 11, | |
"month": 6 | |
}, { | |
"title": "GitHub Copilot is Amazing - It Won't Replace Developers", | |
"slug": "https://unicorn-utterances.com/posts/github-copilot-wont-replace-devs/", | |
"year": 2021, | |
"day": 30, | |
"month": 4 | |
}]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment