public async Task ShouldLoadEntryIntoHtmlFile() { var projectInfo = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType()); var blobContainerName = this.TestContext.Properties["blobContainerName"].ToString(); var htmlFile = this.TestContext.Properties["htmlFile"].ToString(); htmlFile = Path.Combine(projectInfo.FullName, htmlFile); var slug = this.TestContext.Properties["slug"].ToString(); var container = cloudStorageAccountClassic.CreateCloudBlobClient().GetContainerReference(blobContainerName); var keys = new AzureBlobKeys(); keys.Add <BlogEntry>(i => i.Slug); var repository = new BlogRepository(keys, container); var blogEntry = await repository.LoadSingleAsync <BlogEntry>(slug); var h2 = new XElement("h2", blogEntry.Title); var contentHtml = string.Format("<content>{0}</content>", blogEntry.Content); contentHtml = HtmlUtility.ConvertToXml(contentHtml); var content = XElement.Parse(contentHtml); var html = File.ReadAllText(htmlFile); var xDoc = XDocument.Parse(html); var body = xDoc.Root.Element("body"); body.Value = string.Empty; body.Add(h2); body.Add(content.Elements()); File.WriteAllText(htmlFile, xDoc.ToString()); }
/// <summary> /// Reduces <see cref="BlogEntry.Content"/> to an extract. /// </summary> /// <param name="data">The data.</param> /// <returns></returns> public static BlogEntry WithContentAsExtract(this BlogEntry data) { if (data == null) { return(null); } #region functional members: Func <string, string> getExtract = content => { if (string.IsNullOrEmpty(content)) { return(null); } traceSource?.TraceVerbose("Getting Blog Entry Content..."); traceSource?.TraceVerbose("Getting Blog Entry Slug: {0}", data.Slug); var limit = default(int); try { limit = 255; if (XmlUtility.IsXml(content)) { content = HtmlUtility.ConvertToXml(content); content = content.Replace(" ", string.Empty); // TODO: this should be in HtmlUtility.ConvertToXml(). var rootElement = XElement.Parse(string.Format("<root>{0}</root>", content)); content = XObjectUtility.JoinFlattenedXTextNodes(rootElement); } } catch (Exception ex) { traceSource?.TraceError(ex); } return((content.Length > limit) ? string.Format("{0}...", content.Trim().Substring(0, limit - 1)) : content); }; #endregion data.Content = getExtract(data.Content); return(data); }
public async Task ShouldExpandUris() { var projectDirectoryInfo = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType()); #region test properties: var jsonPath = this.TestContext.Properties["jsonPath"].ToString(); jsonPath = projectDirectoryInfo.ToCombinedPath(jsonPath); this.TestContext.ShouldFindFile(jsonPath); var twitterHost = this.TestContext.Properties["twitterHost"].ToString(); #endregion var json = File.ReadAllText(jsonPath); var blogEntry = JsonConvert.DeserializeObject <BlogEntry>(json); Assert.IsNotNull(blogEntry, "The expected Blog Entry is not here."); var xml = HtmlUtility.ConvertToXml(blogEntry.Content); var xd = XDocument.Parse(string.Format("<content>{0}</content>", xml)); var twitterAnchors = xd.Descendants("a") .Where(i => i.Value.Contains(string.Format("://{0}/", twitterHost))); Assert.IsTrue(twitterAnchors.Any(), string.Format("The expected {0} anchors are not here.", twitterHost)); var tasks = twitterAnchors.Select(async i => { var uri = new Uri(i.Attribute("href").Value, UriKind.Absolute); this.TestContext.WriteLine("wrapped uri: {0}", uri); var response = await httpClient.GetAsync(uri); this.TestContext.WriteLine("HttpStatusCode: {0}", response.StatusCode); Assert.IsTrue(response.IsMovedOrRedirected(), "The expected status code is not here."); var expandedUri = response.Headers.Location; this.TestContext.WriteLine("expanded URI: {0}", expandedUri); i.ExpandTwitterAnchor(expandedUri.OriginalString, twitterHost); }); await Task.WhenAll(tasks); var xhtml = xd.ToString().Replace(@"</a><a", "</a> <a"); this.TestContext.WriteLine("XHTML:\n{0}", xhtml); }
public async Task ShouldGenerateBlogEntry() { var projectInfo = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType()); var webProjectInfo = this.TestContext.ShouldGetConventionalProjectDirectoryInfo(this.GetType()); #region test properties: var blobContainerName = this.TestContext.Properties["blobContainerName"].ToString(); var entryHeaderElement = this.TestContext.Properties["entryHeaderElement"].ToString(); var entryPath = this.TestContext.Properties["entryPath"].ToString(); entryPath = projectInfo.ToCombinedPath(entryPath); this.TestContext.ShouldFindFile(entryPath); var entryOutputPath = this.TestContext.Properties["entryOutputPath"].ToString(); entryOutputPath = projectInfo.ToCombinedPath(entryOutputPath); this.TestContext.ShouldFindFile(entryOutputPath); #endregion var container = cloudStorageAccountClassic.CreateCloudBlobClient().GetContainerReference(blobContainerName); var keys = new AzureBlobKeys(); keys.Add <BlogEntry>(i => i.Slug); var repository = new BlogRepository(keys, container); var entry = File.ReadAllText(entryPath); entry = HtmlUtility.ConvertToXml(entry); var xdEntry = XDocument.Parse(entry); var body = xdEntry.Root.Element("body"); Assert.IsNotNull(body, "The expected body is not here."); var title = body.Element(entryHeaderElement).GetInnerXml(); var content = new XElement("body", body .Elements() .Where(i => !i.Name.LocalName.Equals(entryHeaderElement)) ).GetInnerXml(); var blogEntry = (new BlogEntry { Content = content, InceptDate = DateTime.Now, IsPublished = true, Title = title }).WithDefaultSlug(); if (await repository.HasEntityAsync <BlogEntry>(blogEntry.Slug)) { var previousEntry = await repository.LoadSingleAsync <BlogEntry>(blogEntry.Slug); Assert.IsNotNull(previousEntry, "The expected previous entry is not here."); blogEntry.InceptDate = previousEntry.InceptDate; } await repository.SaveEntityAsync(blogEntry); Assert.IsTrue(await repository.HasEntityAsync <BlogEntry>(blogEntry.Slug), "The expected Blog Entry is not in the Repository."); var json = blogEntry.ToJson(useJavaScriptCase: true); File.WriteAllText(entryOutputPath, json); }
public async Task ShouldExpandUrisFromNewEntry() { var projectDirectoryInfo = this.TestContext.ShouldGetProjectDirectoryInfo(this.GetType()); #region test properties: var htmlPath = this.TestContext.Properties["htmlPath"].ToString(); htmlPath = projectDirectoryInfo.ToCombinedPath(htmlPath); this.TestContext.ShouldFindFile(htmlPath); var twitterHost = this.TestContext.Properties["twitterHost"].ToString(); var hootsuiteHost = this.TestContext.Properties["hootsuiteHost"].ToString(); #endregion #region functional members: string expandAmpersand(string s) { s = s .Replace(" & ", " & ") .Replace("Q&A", "Q&A") .Replace("R&B", "R&B") ; return(s); } string expandArrows(string s) { s = s .Replace("<<", "<<") .Replace(">>", ">>") .Replace("=>", "=>") .Replace("<=", "<=") .Replace("->", "->") .Replace("<-", "<-") .Replace("<Action<", "<Action<") .Replace("<Func<", "<Func<") .Replace("Action<", "Action<") .Replace("Func<", "Func<") ; return(s); } string expandComparisonOperatorGlyphs(string s) { s = s .Replace(" < ", " < ") .Replace(" > ", " > ") ; return(s); } string expandGenericNotation(string s) { s = s.Replace("<T>", "<T>"); return(s); } async Task <Uri> expandUri(Uri expandableUri) { var messageBuilder = new StringBuilder(); messageBuilder.AppendLine($"expanding wrapped URI: {expandableUri}..."); var response = await httpClient.GetAsync(expandableUri); messageBuilder.AppendLine($"HttpStatusCode: {response.StatusCode}"); if (response.IsMovedOrRedirected()) { var expandedUri = response.Headers.Location; messageBuilder.AppendLine($"expanded URI: {expandedUri}"); this.TestContext.WriteLine(messageBuilder.ToString()); return(expandedUri); } else { messageBuilder.AppendLine($"WARNING: the expected status code is not here. Headers location: {response.Headers.Location}"); this.TestContext.WriteLine(messageBuilder.ToString()); return(expandableUri); } } bool isHost(string context, string host) => context.Contains($"://{host}/"); async Task processTwitterAnchorsAsync(XDocument xDocument) { this.TestContext.WriteLine("looking for Twitter anchors of the form <a*>*://{0}//*</a>...", twitterHost); var twitterAnchors = xDocument.Descendants("a").Where(i => isHost(i.Value, twitterHost)); var tasks = twitterAnchors.Select(async i => { var uri = new Uri(i.Attribute("href").Value, UriKind.Absolute); var expandedUri = await expandUri(uri); if (isHost(expandedUri?.OriginalString, hootsuiteHost)) { expandedUri = await expandUri(expandedUri); } i.ExpandTwitterAnchor(expandedUri.OriginalString, twitterHost); }); await Task.WhenAll(tasks); } string removeAnchorLineBreaks(string s) { var regexes = new[] { new Regex(@"(\r\n?\s*)<a "), new Regex(@"</a>\s*(\r\n?\s*)") }; regexes.ForEachInEnumerable(re => { s = re.Replace(s, match => { if (match.Groups.Count() != 2) { return(match.Value); } return(match.Value.Replace(match.Groups[1].Value, " ")); }); }); return(s); } string removeImageLineBreaks(string s) { var re = new Regex(@"(\r\n?\s*)(<img [^>]+/>)\s*(\r\n?\s*)"); s = re.Replace(s, match => { if (match.Groups.Count() != 4) { return(match.Value); } return($" {match.Groups[2].Value} "); }); return(s); } string removeNewLineAndSpaceAfterParagraphElement(string s) { var re = new Regex(@"(\<p\>)\r\n\s+"); re.Matches(s).OfType <Match>().ForEachInEnumerable(i => { s = s.Replace(i.Value, i.Groups[1].Value); }); return(s); } string removeNonBreakingSpace(string s) { s = s.Replace(" ", " "); return(s); } #endregion var html = File.ReadAllText(htmlPath); html = expandAmpersand(html); html = expandArrows(html); html = expandComparisonOperatorGlyphs(html); html = expandGenericNotation(html); html = removeNonBreakingSpace(html); html = HtmlUtility.ConvertToXml(html); var xd = XDocument.Parse(html); await processTwitterAnchorsAsync(xd); html = xd.ToString(); html = removeAnchorLineBreaks(html); html = removeImageLineBreaks(html); html = removeNewLineAndSpaceAfterParagraphElement(html); File.WriteAllText(htmlPath, html); }