.author{grid-column:1;grid-row:3;align-content:flex-start}.Metadata_root__oCstk>.description,.Metadata_root__oCstk>.pills-list,.Metadata_root__oCstk>.stats,.Metadata_root__oCstk>.title{grid-column:1/span 2}.Metadata_root__oCstk>.title{margin-bottom:8px}.Metadata_root__oCstk>.stats{margin-bottom:12px}.Metadata_root__oCstk>.description{margin-top:12px}.Metadata_root__oCstk>.pills-list{margin-block-start:20px;margin-block-end:18px}.Metadata_root__oCstk>.actions{display:none}@media screen and (max-width:928px){.Metadata_root__oCstk{margin-block:8px 20px}.Metadata_root__oCstk>.stats{margin-bottom:16px}.Metadata_root__oCstk>.description{margin-top:0}.Metadata_root__oCstk>.author{grid-column:2;grid-row:5;margin-block-start:12px;margin-block-end:6px;justify-content:end}.Metadata_root__oCstk>.actions{display:flex;margin-top:16px;grid-column:1;grid-row:5}.Metadata_root__oCstk>.pills-list{margin-block-start:16px;margin-block-end:0}}.Metadata_root__oCstk .metadata-recs{grid-column:1/-1}.Heading_heading__3MAvZ{color:var(--blue-gray-900)}.Heading_h1__3k7S2{font-size:32px;font-weight:700}.Heading_h2__f9yvs{font-size:28px;font-weight:600}.Heading_h3__f1djd{font-size:24px}.Heading_h4__7tfLE{font-size:20px}.Heading_h5__jVM0l{font-size:16px;font-weight:400}.Heading_h6__uUTrd{font-size:14px;font-weight:400}.Title_root__svkHQ{color:var(--blue-gray-900);font-size:clamp(26px,1vw + 1rem,28px);font-weight:600;line-height:1.25;min-width:0;word-break:break-word}.Stats_root__p_BoZ{flex-wrap:wrap;display:flex;align-items:center;-moz-column-gap:6px;column-gap:6px;color:var(--blue-gray-600);white-space:nowrap;font-size:16px}.Stats_leftContent__588PR,.Stats_rightContent__8d0AF{display:flex;gap:6px}.Stats_root__p_BoZ span{font-size:16px}.Stats_root__p_BoZ .Stats_aiTag__zTzW8{margin-left:10px}@media screen and (max-width:928px){.Stats_root__p_BoZ span{line-height:1.5}.Stats_root__p_BoZ.Stats_extendedMetadata__wb62p .Stats_leftContent__588PR{width:100%}.Stats_root__p_BoZ .Stats_aiTag__zTzW8{margin:8px 0 0}.Stats_root__p_BoZ.Stats_extendedMetadata__wb62p .Stats_formatTypesBullet__xDv0L{display:none}}.Likes_root__WVQ1_{cursor:pointer;transition:color .2s ease-in-out;border-radius:4px}.Likes_root__WVQ1_:hover{color:var(--blue-gray-700)}.Tooltip_root__7FS0Y{background:var(--midnight-green-dark);border-radius:4px;box-shadow:0 .5px 5px rgba(0,0,0,.04),0 4px 11px rgba(0,0,0,.2);color:var(--white);font-weight:400;font-size:12px;line-height:15px;padding:6px 8px;opacity:0;visibility:hidden;animation:Tooltip_show__qVG5k .2s ease-in-out forwards;z-index:var(--popup-index)}.Tooltip_triggerWrapper___S2HG{flex-shrink:0;position:relative;align-items:center;justify-content:center}@keyframes Tooltip_show__qVG5k{to{opacity:1;visibility:visible}}.Tooltip_large__J4Fvl{padding:16px;display:flex;flex-direction:column;background:#fff;color:var(--black)}.AITag_tag__Xx37c{padding:0 12px;height:25px;border-radius:16px;background-color:#f0f2f9;color:#16171b;display:inline-flex;gap:8px;font-size:14px;line-height:1.5;font-weight:600}.AITag_tooltipContent__7JZR_{width:332px}.Author_root___6Bx5{--link-color:var(--blue-gray-800);position:relative;display:flex;align-items:center;gap:8px}.Author_link___lVxw{z-index:1;color:var(--link-color);font-weight:600;display:block}.Author_link___lVxw:before{content:"";position:absolute;inset:0}.Author_follow__Lw4TS{z-index:1}@media screen and (max-width:928px){.Author_link___lVxw:hover{color:var(--blue-gray-800)}}.Avatar_root__GNWHY{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;background-color:var(--white);color:var(--blue-gray-300);border-radius:50%;font-size:16px;font-weight:600;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;overflow:hidden}.Avatar_initials__EJfVt{color:var(--white);transition:background-color .2s ease-in-out}.Avatar_initials__EJfVt,.Avatar_initials__EJfVt:hover{background-color:var(--blue-gray-600)}.Avatar_image__Bbtll{width:100%;height:100%;-o-object-fit:cover;object-fit:cover}.FollowButton_root__FxpBi{display:inline-flex;background-color:transparent;border:1px solid transparent;border-radius:4px;font-size:12px;padding:1px 6px;transition:background-color .2s ease-in-out,border-color .2s ease-in-out;cursor:pointer}.FollowButton_following__xKCww{border-color:#bf5905;color:#bf5905}.FollowButton_following__xKCww:hover{background-color:#ffead7;border-color:rgba(191,89,5,.5)}.FollowButton_follow__d_6u5{border-color:var(--celadon-blue-dark);color:var(--celadon-blue-dark)}.FollowButton_follow__d_6u5:hover{background-color:#eaf7ff;border-color:rgba(2,126,176,.5)}@media screen and (max-width:928px){.FollowButton_root__FxpBi{display:none}}.Description_root__kt4uq{--line-height:26px;position:relative}.Description_root__kt4uq.Description_clamped__PaV_1{padding-bottom:25px}.Description_root__kt4uq.Description_clamped__PaV_1 .Description_wrapper__hYE9_{mask-image:linear-gradient(to bottom,var(--white),transparent);-webkit-mask-image:linear-gradient(to bottom,var(--white),transparent)}.Description_wrapper__hYE9_{min-height:var(--line-height);display:-webkit-box;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}.Description_noClamp__1z7c5,.Description_wrapper__hYE9_.Description_expanded__lRamt{-webkit-line-clamp:unset;-webkit-mask-image:none;mask-image:none}.Description_wrapper__hYE9_.Description_expanded__lRamt{height:auto}.Description_wrapper__hYE9_ p{color:var(--blue-gray-600);font-size:18px;line-height:var(--line-height);white-space:pre-wrap;word-break:break-word}.Description_more__ChrRK{position:absolute;padding:0;bottom:0;height:26px}.Description_less__BvWbY{padding:0}.Description_hidden__a9QZJ{display:none}@media screen and (max-width:928px){.Description_more__ChrRK{right:0;background-color:#fff}.Description_root__kt4uq.Description_clamped__PaV_1{padding-bottom:0}.Description_less__BvWbY,.Description_more__ChrRK{height:var(--line-height)}}.PillsList_root__2EydN{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.Pill_root__IqOYH{--bg:color-mix(in srgb,var(--celadon-blue-dark),90% transparent);--color:var(--blue-gray-800);height:40px;display:inline-flex;align-items:center;gap:6px;background-color:var(--bg);border-radius:100vmax;color:var(--color);font-size:16px;font-weight:600;padding-inline:16px;transition:color .2s ease-in-out,background-color .2s ease-in-out,filter .2s ease-in-out;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;text-decoration:none;white-space:nowrap}.Pill_root__IqOYH:not(.Pill_selected__VPtHm):hover{filter:brightness(.6)}.Pill_root__IqOYH.Pill_selected__VPtHm{--bg:var(--blue-gray-900);--color:var(--white)}@media screen and (max-width:520px){.Pill_root__IqOYH{height:28px;padding-inline:12px;gap:4px;font-size:12px}.Pill_icon__xE_Cg{--size:18px!important}}.Actions_root__00yIC{display:flex;align-items:baseline;gap:6px}.ReadingModeTooltip_root__laf5h{width:277px}.ToggleButtonGroup_root__vtGE_{display:inline-flex;gap:4px;padding:5px 8px;border-radius:4px;background-color:var(--white);border:1px solid var(--blue-gray-200)}.ToggleButton_root__jGx6U{border-radius:4px;background-color:var(--white);color:var(--blue-gray-700);border:none;width:44px;height:34px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}.ToggleButton_selected__51g6d{background-color:var(--blue-gray-200)}.SaveLoggedIn_icon__lk74r{color:var(--blue-gray-700)}.PopoverMenuContent_root__MsRtR{background:var(--white);box-shadow:0 .5px 5px rgba(0,0,0,.04),0 4px 11px rgba(0,0,0,.2);border-radius:4px;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;z-index:1000;overflow-y:auto}.PopoverMenuContent_root__MsRtR.PopoverMenuContent_visible__O86I_{opacity:1;visibility:visible;transition-delay:0s}.PopoverMenuItem_item__iazpP{width:100%;display:flex;align-items:center;background-color:transparent;color:inherit;cursor:pointer;font-size:inherit;line-height:24px;padding:12px 16px;white-space:nowrap}.PopoverMenuItem_item__iazpP.PopoverMenuItem_highlight__inbqK,.PopoverMenuItem_item__iazpP:hover{background-color:var(--blue-gray-100)}.PopoverMenuSeparator_separator__UpSGw{width:calc(100% - 32px);height:1px;background-color:var(--blue-gray-300);margin:8px 16px}.SavePopover_popover__mZhIY{width:290px;max-height:220px;color:var(--blue-gray-800);font-weight:400;padding:6px 8px;font-size:12px;line-height:15px;overflow:hidden}.SavePopover_saveToNewList__MVBSu{display:flex;align-items:center;gap:6px;color:var(--midnight-green-dark);font-weight:600;font-size:18px;line-height:24px;padding:10px;width:100%}.SavePopover_addIcon__aJLJ3{color:var(--blue-gray-800)}.SavePopover_horizontalSeparator__bsI6Y{border:none;border-bottom:1px solid var(--blue-gray-300);margin:8px 0}.SavePopover_listsContainer__tGGtp{height:100%;max-height:144px;display:flex;flex-direction:column;align-items:center;overflow-y:auto}.SavePopover_saveToList__Fsfl9{color:var(--blue-gray-800);font-weight:400;font-size:16px;justify-content:space-between;line-height:24px;padding:12px 14px 12px 16px;width:100%}.SavePopover_saveToList__Fsfl9 span:not(.SavePopover_clampLines__Blxtm){display:flex;flex-direction:row;gap:35px;justify-content:space-between}.SavePopover_saveToList__Fsfl9 span.SavePopover_clampLines__Blxtm{display:inline-block;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap;width:180px}.SavePopover_listIcon__OVsXv{color:var(--blue-gray-900)}.SavePopover_noSavedLists__0Mh_H{color:var(--blue-gray-600);font-weight:400;font-size:16px;line-height:24px;padding:12px 16px;text-align:center}.SaveOptionsDrawer_drawerTrigger__Gb7nK{box-shadow:none;border:none;border-radius:4px;position:relative;z-index:7}.SaveOptionsDrawer_drawerTrigger__Gb7nK:active,.SaveOptionsDrawer_drawerTrigger__Gb7nK:active:focus,.SaveOptionsDrawer_drawerTrigger__Gb7nK:hover{border:none;box-shadow:none;background-color:transparent}.SaveOptionsDrawer_drawerTrigger__Gb7nK:focus{background:transparent}.SaveOptionsDrawerContent_drawerContent__J5JTL{margin:16px}.SaveOptionsDrawerContent_separator__UW5Rz{margin:8px 0}.SaveOptionsDrawerContent_itemsContainer__tCXw5{display:flex;flex-direction:column;margin-top:0;max-height:144px;overflow-y:auto}.SaveOptionsDrawerContent_drawerActionSecondary__7cq8j{font-weight:400;line-height:24px;padding:0 8px;margin:8px 0;width:100%}.SaveOptionsDrawerContent_drawerActionSecondary__7cq8j span{color:var(--blue-gray-800);justify-content:space-between;gap:30px}.SaveOptionsDrawerContent_drawerActionPrimary____QuR span{gap:14px}.SaveOptionsDrawerContent_drawerActionPrimary____QuR{color:var(--blue-gray-800);display:flex;font-weight:400;line-height:24px;align-items:center;justify-content:flex-start;padding:8px 4px;margin:14px 0}.SaveOptionsDrawerContent_drawerActionPrimary____QuR.SaveOptionsDrawerContent_newListButton__VzhfD{font-size:16px;font-weight:600}.SaveOptionsDrawerContent_addIcon__Wb2cq{color:var(--blue-gray-800)}.SaveOptionsDrawerContent_drawerActionSecondary__7cq8j span.SaveOptionsDrawerContent_clampLines__zfkfI{display:inline-block;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap;width:calc(100vw - 115px)}.SaveOptionsDrawerContent_listIcon__5dcfC{color:var(--blue-gray-900)}.SaveOptionsDrawerContent_noSavedLists__cpUBY{color:var(--blue-gray-600);font-weight:400;font-size:16px;line-height:24px;padding:12px 16px;text-align:center}.Separator_root__70Ime{--orientationMargin:0;background-color:var(--blue-gray-200);flex-shrink:0}.Separator_horizontal__czVEa{width:calc(100% - var(--orientationMargin) * 2);height:1px}.Separator_vertical__JYCCK{width:1px;height:calc(100% - var(--orientationMargin) * 2)}.SaveLoggedOut_icon__ny9X2{color:var(--blue-gray-700)}.Dropdown_root__Z78h8{display:inline-block;position:relative;color:inherit}.DropdownTrigger_trigger__SzsBj{display:flex;align-items:center;justify-content:center;background:transparent;border:none;font-size:inherit;padding:0;margin:0;cursor:pointer}.DropdownTrigger_trigger__SzsBj:active,.DropdownTrigger_trigger__SzsBj:focus,.DropdownTrigger_trigger__SzsBj:hover{background:transparent}.DropdownContent_content__3daFs{position:absolute;display:flex;flex-direction:column;align-items:flex-start;padding:8px 0;background-color:#fff;box-shadow:0 .5px 5px rgba(0,0,0,.039),0 3.75px 11px rgba(0,0,0,.19);border-radius:4px;color:var(--blue-gray-800);opacity:0;visibility:hidden;transition:transform .15s,opacity .15s,visibility 0s linear .15s;transform:scale(.95);z-index:var(--dropdown-index)}.DropdownContent_bottom-left__gioqM{top:calc(100% + 6px);left:0;transform-origin:top left}.DropdownContent_bottom-right__QJ94h{top:calc(100% + 6px);right:0;transform-origin:top right}.DropdownContent_top-left__O3Ryp{bottom:calc(100% + 6px);left:0;transform-origin:bottom left}.DropdownContent_top-right___Qe45{bottom:calc(100% + 6px);right:0;transform-origin:bottom right}.DropdownContent_content__3daFs.DropdownContent_open__6VoiP{visibility:visible;opacity:1;transform:none;transition-delay:0s}.DropdownItem_item__Sv0GT{width:100%;display:flex;align-items:center;background-color:transparent;color:inherit;cursor:pointer;font-size:inherit;line-height:24px;padding:12px 16px;white-space:nowrap}.DropdownItem_highlight__jO3zg,.DropdownItem_item__Sv0GT:hover{background-color:var(--blue-gray-100)}.DropdownSeparator_separator__EN82n{width:calc(100% - 32px);height:1px;background-color:var(--blue-gray-300);margin:8px 16px}.MoreDropdownButton_moreOptionsDropdown__GK_Uw{display:flex;align-items:center;justify-content:center;width:32px;height:32px}.MoreDropdownButton_item__t4HmI{gap:12px}.MoreDropdownButton_moreOptionsIcon__TpJLA{color:var(--blue-gray-700)}.MoreDropdownButton_icon__DxfY4{color:var(--blue-gray-800)}.MoreDropdownButton_tooltip__az4od{white-space:nowrap}.MetadataToolbar_root__c03ao{--shadow-opacity:0;display:grid;height:var(--metadata-toolbar-height);background-color:var(--white);z-index:var(--header-index)}.MetadataToolbar_wrapper__r7XEc{position:relative;display:grid;grid-template-columns:minmax(0,1fr) max-content 1fr;align-items:center;padding-inline-end:20px;padding-block:16px}.MetadataToolbar_underline__QQn0C{grid-column:1/-1;grid-row:2;height:1px;position:absolute;bottom:0;right:-20px;left:-30vw;background-color:var(--blue-gray-200);box-shadow:0 2px 4px 0 rgba(0 0 0/var(--shadow-opacity));animation:MetadataToolbar_reveal-shadow__5yBxP linear both;animation-timeline:scroll(block);animation-range:150px 450px}.MetadataToolbar_verticalMode__Bh759{position:sticky}.MetadataToolbar_title__jfTWv{font-size:18px;font-weight:600}.MetadataToolbar_actions__FB33C{width:-moz-max-content;width:max-content;justify-self:flex-end;display:flex;grid-column-end:-1}.MetadataToolbar_pageNumber__i6Bhj{display:inline-flex;align-items:center;margin-inline:1em;height:44px}.MetadataToolbar_isInReadingModeVariant__XMDcr{align-items:flex-start}.MetadataToolbar_isInReadingModeVariant__XMDcr .MetadataToolbar_downloadButton__ncS7o>button{padding:0 15px}@media screen and (max-width:928px){.MetadataToolbar_root__c03ao{display:none}}@media screen and (min-width:929px) and (max-width:1249px){.MetadataToolbar_isInReadingModeVariant__XMDcr .MetadataToolbar_pageNumber__i6Bhj{display:none}}@keyframes MetadataToolbar_reveal-shadow__5yBxP{to{--shadow-opacity:0.122}}.DownloadButton_root__adY00{margin-left:auto;display:inline-grid;gap:6px;justify-items:center;flex-shrink:0}.DownloadButton_savedStyling__k18od{font-weight:600;font-size:18px}.DownloadMultipleFormatDrawer_root__CWFxX{width:100%;padding:0 24px}.DownloadMultipleFormatDrawer_drawerHeading__8LnFw{margin:16px 0}.DownloadMultipleFormatDrawer_drawerContent__y815X{width:100%;padding:24px 0}.DownloadMultipleFormatDrawer_drawerRadioButtons__I_lQ4 label{margin-bottom:20px}.DownloadMultipleFormatDrawer_drawerRadioButtons__I_lQ4{margin-bottom:4px}.Fieldset_root__L2NQU{display:grid;padding:0;border:0}.Fieldset_root__L2NQU legend{display:none}.DownloadMultipleFormatPopover_popoverContent__IJudF{min-width:185px}.FadeInOut_root__v7Efq{position:relative;min-width:0;background-color:var(--snow-gray);padding:20px}.FadeInOut_root__v7Efq.FadeInOut_isInfographic__PdX2K{background-color:unset;padding-inline-start:0;padding-inline-end:20px}@media (max-width:928px){.FadeInOut_root__v7Efq,.FadeInOut_root__v7Efq.FadeInOut_isInfographic__PdX2K{background-color:unset;padding-inline:2px}}.FadeInOut_in__9dYWz{animation:FadeInOut_fadeIn__14JD0 .5s}.FadeInOut_out___eDZk{animation:FadeInOut_fadeOut__ILQaD .4s}@keyframes FadeInOut_fadeIn__14JD0{0%{opacity:0}to{opacity:1}}@keyframes FadeInOut_fadeOut__ILQaD{0%{opacity:1}to{opacity:0}}.VerticalSlideOverlayed_root__9Thd4{position:relative}.VerticalSlideOverlayed_root__9Thd4 .vertical-slide-image{position:absolute;inset:0}.VerticalSlideOverlayed_active___p5f2 .vertical-slide-image{opacity:.4}.VerticalSlideOverlayed_active___p5f2{background-color:#000}.VerticalSlideImage_image__VtE4p{width:100%;height:100%;-o-object-fit:contain;object-fit:contain;box-shadow:0 0 0 1px var(--blue-gray-200);border-radius:8px;transition:opacity .3s cubic-bezier(.2,0,0,1);opacity:.9;overflow:clip;background-color:var(--blue-gray-600);animation-name:VerticalSlideImage_pulse__OPBSn;animation-direction:alternate;animation-duration:1s;animation-iteration-count:infinite;animation-timing-function:ease-in-out}.VerticalSlideImage_image__VtE4p.VerticalSlideImage_isLoaded__r4xAa{opacity:1;animation:none;background-color:unset}@keyframes VerticalSlideImage_pulse__OPBSn{0%{opacity:.09}to{opacity:.15}}.SlideScrollDetector_root__AIK38{position:relative;height:100%;width:100%;pointer-events:none}.SlideScrollDetector_detector__rhkyk{position:absolute;top:0;left:0;height:1px;width:100%}.SlideScrollDetector_detector1__NmF8s{top:25%}.SlideScrollDetector_detector2__32XWr{top:75%}.SlideActions_root__fB9Q4{position:absolute;right:0;top:0;display:grid;grid-template-columns:40px;grid-template-rows:40px 40px;grid-column-gap:12px;-moz-column-gap:12px;column-gap:12px;grid-row-gap:12px;row-gap:12px;padding:12px;justify-content:flex-end;align-items:center;text-align:end}.SlideActions_active__aD_e1{grid-template-columns:max-content 40px}.SlideActions_button__o8UXk{background-color:var(--blue-gray-100);color:var(--blue-gray-700)}.VerticalSlide_infographic__ij1FA,.VerticalSlide_root__jU_9r{position:relative}.VerticalSlide_root__jU_9r .vertical-slide-image{position:absolute;inset:0}.VerticalSlide_infographic__ij1FA .vertical-slide-image{position:unset}.VerticalPlayer_root__K8_YS{position:relative;display:grid;grid-template-columns:minmax(0,1fr);grid-gap:24px;gap:24px}@media screen and (max-width:928px){.VerticalPlayer_root__K8_YS{gap:8px}}.FreestarVideoAd_root__KDWgl{min-width:0;flex-shrink:0;aspect-ratio:16/9}.VerticalInterstitialAdWrapper_root__LxQh8{container-type:inline-size;position:relative;display:grid;min-height:280px;overflow-x:clip}.VerticalInterstitialAdWrapper_root__LxQh8:has(.interstitial-ad-container.has-fetched):not(:has(.interstitial-ad-container.has-fetched .freestar-ad-container:not(.unfilled))){display:none}.VerticalInterstitialAdWrapper_root__LxQh8:has(div.interstitial-ad-container.has-fetched div.freestar-ad-container.filled div),.VerticalInterstitialAdWrapper_root__LxQh8:has(div.interstitial-ad-container.has-fetched div.freestar-ad-container.unfilled iframe){display:block!important}.VerticalInterstitial_root__Dunl7{display:none}@media (max-width:520px){.VerticalInterstitial_root__Dunl7:not(.VerticalInterstitial_inVariant__xB9lL){display:block}}@container (max-width: 480px){.VerticalInterstitial_root__Dunl7.VerticalInterstitial_inVariant__xB9lL{display:block}}.FreestarAdContainer_root__qPPC_{position:relative;display:grid;place-content:center}.FreestarAdContainer_root__qPPC_.FreestarAdContainer_withFallback__A4lgm{aspect-ratio:var(--fallback-aspect-ratio)}.FreestarAdContainer_fallback__WreT9{position:absolute;inset:0;grid-template-columns:unset;place-content:center}.AdFallback_root__uAXsl{display:grid;justify-items:center;grid-template-columns:1fr;z-index:0}.MultipleIncontentSmall_root__x58Hs{display:none;grid-template-columns:repeat(auto-fit,minmax(max-content,300px));place-content:center;gap:16px}@media (min-width:521px){.MultipleIncontentSmall_root__x58Hs:not(.MultipleIncontentSmall_inVariant__uf1S8){display:grid}}@container (max-width: 616px){.MultipleIncontentSmall_root__x58Hs:not(.MultipleIncontentSmall_inVariant__uf1S8) .freestar-ad-container:nth-of-type(2){display:none}}@container (min-width: 616px) and (max-width: 688px){.MultipleIncontentSmall_root__x58Hs.MultipleIncontentSmall_inVariant__uf1S8{display:grid}}.MultipleIncontentWide_root__4WD8U{display:none;place-content:center}@container (min-width: 480px) and (max-width: 616px){.MultipleIncontentWide_root__4WD8U{display:grid}}.MultipleIncontentLarge_root__pGIAn{display:none;grid-template-columns:repeat(auto-fit,minmax(max-content,336px));place-content:center;gap:16px}@container (min-width: 688px){.MultipleIncontentLarge_root__pGIAn{display:grid}}.SlideRecs_root__likA5{--card-hover-background:var(--blue-gray-200);display:flex;flex-direction:column;gap:20px}.SlideRecs_root__likA5 h2{font-size:24px;font-weight:600}@media (max-width:1050px){.SlideRecs_root__likA5{display:none}}.SlideRecs_root__likA5 .SlideRecs_cards__Lbxtt{display:grid;grid-gap:16px;gap:16px}.SlideRecs_root__likA5 .SlideRecs_card__txc2D{position:relative;grid-template-columns:180px 1fr;grid-template-rows:auto;gap:16px}.SlideRecs_root__likA5 .slideshow-thumbnail{box-shadow:0 0 0 1px var(--blue-gray-200)}.SlideRecs_root__likA5 .slideshow-card-content{padding-block:0;padding-inline-end:36px;gap:8px}.SlideRecs_root__likA5 .slideshow-title{line-height:1;margin-block-end:0}.SlideRecs_root__likA5 .SlideRecs_wrapper__21j_w{display:flex;gap:8px;align-items:center}.SlideRecs_root__likA5 .SlideRecs_wrapper__21j_w a,.SlideRecs_root__likA5 .SlideRecs_wrapper__21j_w span{font-size:12px}.SlideRecs_root__likA5 .SlideRecs_save__RR8dD{position:absolute;top:8px;right:8px}.SlideRecs_root__likA5 .SlideRecs_author__zlhWO{color:var(--blue-gray-700);font-weight:600;text-decoration:none;z-index:2}.SlideRecs_root__likA5 .SlideRecs_author__zlhWO:first-letter{text-transform:uppercase}.SlideRecs_root__likA5 .SlideRecs_card__txc2D .slideshow-title{font-size:16px}.SlideRecs_root__likA5 .SlideRecs_description__0bGsx{color:var(--blue-gray-700);font-size:14px;display:-webkit-box;overflow:hidden;line-clamp:1;-webkit-line-clamp:1;-webkit-box-orient:vertical}.SlideRecs_root__likA5 .SlideRecs_dot__Db7HR{font-size:16px}.SlideRecs_root__likA5 .SlideRecs_tags__RCA1q{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.SlideRecs_root__likA5 .SlideRecs_tags__RCA1q span{display:grid;place-content:center;height:20px;background-color:var(--alice-blue-600);border-radius:100vmax;color:var(--blue-gray-700);font-size:11px;font-weight:600;padding-inline:12px;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap;text-transform:capitalize;z-index:2}.RecWithPopover_root__iNXfW{width:330px;display:flex;flex-direction:column;gap:8px}.RecWithPopover_root__iNXfW>*{line-height:18px}.RecWithPopover_root__iNXfW h3{font-size:16px;font-weight:600}.RecWithPopover_root__iNXfW p{flex:1 1;color:var(--blue-gray-600);font-size:14px;max-width:100%;display:-webkit-box;line-clamp:5;-webkit-line-clamp:5;-webkit-box-orient:vertical;overflow-y:hidden}.RecWithPopover_hasSiblingsWithPopover__dlaxN{border-radius:4px;padding:12px}.RecWithPopover_hasPopover__G8_rr:hover:not(:has(:is(.save:hover,.author:hover))){background-color:var(--card-hover-background)}.SlideshowCard_root__pD8t4{position:relative;display:grid;grid-template-rows:max-content minmax(0,1fr);grid-template-columns:minmax(0,1fr);align-content:flex-start;color:var(--blue-gray-600)}.SlideshowCard_root__pD8t4:hover .SlideshowCard_thumb__86aJk{scale:1.02}.SlideshowCard_content__xh7kV{display:flex;flex-direction:column;-moz-column-gap:8px;column-gap:8px;padding:16px 0}.SlideshowCardLink_root__p8KI7{position:absolute;inset:0;z-index:1;margin:4px}.Thumbnail_root__qLW0K{--ease:cubic-bezier(0.2,0,0,1);position:relative;background-color:var(--blue-gray-100);border:1px solid var(--blue-gray-100);border-radius:8px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);scale:1;transition:scale .2s var(--ease);overflow:hidden}.Thumbnail_thumb__UXO3a{--reveal-delay:calc(30ms * var(--index));position:absolute;inset:0;width:100%;height:100%;opacity:0;transition:opacity .3s var(--ease);transition-delay:var(--reveal-delay)}.Thumbnail_loaded__XOJ5p{opacity:1}.Thumbnail_blur__opK6A{filter:blur(8px)}.Thumbnail_cover__1zsIi{-o-object-fit:cover;object-fit:cover}.Thumbnail_contain__K6M0d{-o-object-fit:contain;object-fit:contain}.SlideshowTitle_root__2VccW{display:-webkit-box;color:var(--blue-gray-900);font-size:18px;font-weight:600;line-height:1.2;margin-bottom:8px;white-space:break-spaces;word-break:break-word;-webkit-box-orient:vertical}.RecSaveButton_root__0CS9m{grid-area:d}.RecSaveButton_icon__btwCp{fill:currentColor;z-index:2}@media (max-width:928px){.RecSaveButton_root__0CS9m{display:none}}.SlideshowAuthor_root__IkT1_{color:var(--celadon-blue);font-weight:600;text-decoration:underline;-webkit-text-decoration-color:transparent;text-decoration-color:transparent;text-decoration-thickness:1.5px;text-underline-offset:2px;transition:-webkit-text-decoration-color .2s ease-out;transition:text-decoration-color .2s ease-out;transition:text-decoration-color .2s ease-out,-webkit-text-decoration-color .2s ease-out;z-index:2}.SlideshowAuthor_root__IkT1_:hover{-webkit-text-decoration-color:var(--celadon-blue);text-decoration-color:var(--celadon-blue)}.CountTag_root__y1hE1,.SplitDot_root__lTZDc{color:var(--blue-gray-600);font-weight:400}.SlideshowStats_root__EQOR1{display:flex;align-items:center;gap:6px}.SlideshowStats_text___WD7l{color:var(--blue-gray-600)}.BelowReaderAd_root__NKeGg{margin-top:16px;margin-bottom:60px}.BelowReaderAd_root__NKeGg.BelowReaderAd_desktop__7_JN7{display:block;justify-items:flex-start}.BelowReaderAd_root__NKeGg.BelowReaderAd_mobile__08T3d{display:none}.BelowReaderAd_root__NKeGg .fallback-ad{justify-self:flex-start}@media screen and (max-width:928px){.BelowReaderAd_root__NKeGg.BelowReaderAd_desktop__7_JN7{display:none}.BelowReaderAd_root__NKeGg.BelowReaderAd_mobile__08T3d{display:block;justify-items:center}}.Sidebar_root__1BbNu{width:var(--sidebar-size);max-height:100dvh;overflow:clip scroll;position:sticky;top:var(--metadata-toolbar-offset,0);display:flex;flex-direction:column;padding-inline-start:28px;padding-block-start:32px;padding-block-end:430px}.Sidebar_root__1BbNu.Sidebar_withSidebarAds__0w0dT{max-height:unset;overflow:unset;position:static;top:unset;display:grid;grid-template-rows:repeat(var(--slots),1fr);padding-inline:28px}@media (max-width:1050px){.Sidebar_root__1BbNu,.Sidebar_root__1BbNu.Sidebar_withSidebarAds__0w0dT{display:none}}.AboveRecsAd_root__iTmTR{min-height:280px;margin-block-end:32px}.AboveRecsAd_root__iTmTR .freestar-ad-container{place-content:flex-start}@media (max-width:1050px){.AboveRecsAd_root__iTmTR .freestar-ad-container{place-content:center}}.AboveRecsAd_mobileAd__LYgqf{display:none}.AboveRecsAd_desktopAd__ymykj{display:block}@media (max-width:1050px){.AboveRecsAd_mobileAd__LYgqf{display:block}.AboveRecsAd_desktopAd__ymykj{display:none}}.RailRecommendations_root__zqtZQ{display:flex;flex-direction:column;gap:8px;padding-block-end:24px}.RailRecommendations_title__kt1D2{font-size:24px;color:var(--blue-gray-900);font-weight:600;margin-block-end:20px}@media only screen and (min-width:929px){.RailRecommendations_hidden__7Ct2B{display:none}}.RailCard_root__rZUGY{--card-hover-background:var(--blue-gray-100);width:100%;position:relative;border-radius:8px;padding-block:12px;padding-inline:8px}.RailCard_root__rZUGY.slideshow-card{grid-template-columns:160px minmax(0,1fr);grid-template-rows:auto;gap:12px}.RailCard_root__rZUGY .slideshow-card-content{padding:0}.RailCard_actionContainer__eXUHT{display:grid;grid-template-columns:minmax(0,1fr) 28px;grid-column-gap:8px;-moz-column-gap:8px;column-gap:8px}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-author,.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-stats,.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-title{grid-area:unset}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-author,.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-stats{grid-column:1/-1}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-author{grid-row:2}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-stats{grid-row:3}.RailCard_link__d3BBm{z-index:1}.RailCard_info__Oqm6G{min-width:0;display:flex;flex-direction:column;justify-content:center;word-break:break-word}.RailCard_root__rZUGY .RailCard_title__Tvfiv{font-size:16px;margin-bottom:0;grid-area:a;word-break:break-word}.RailCard_root__rZUGY .RailCard_stats__ZvZms{margin-top:12px}.RailCard_stats__ZvZms .text{font-size:12px}.RailCard_root__rZUGY .RailCard_author__JYeYZ{color:var(--blue-gray-700);margin-top:8px;text-decoration:none}@media screen and (min-width:929px){.RailCard_root__rZUGY:hover{background-color:var(--blue-gray-100)}}.RelatedContent_root__29Np1{background-color:var(--blue-gray-100);border-top:1px solid var(--blue-gray-200);border-bottom:1px solid var(--blue-gray-200);padding-block:32px;position:relative}.RelatedContent_wrapper__riU7l{display:grid;grid-template-columns:minmax(0,1fr);grid-gap:32px;gap:32px;max-width:var(--max-content-width);margin-inline:auto}.RelatedContent_title__QUhpL{text-align:center;font-size:32px;font-weight:700}.RelatedContent_root__29Np1 .bottom-recs{display:grid}.RelatedContent_root__29Np1 .rail-recs{display:none}@media screen and (max-width:520px){.RelatedContent_root__29Np1 .bottom-recs{display:none}.RelatedContent_root__29Np1 .rail-recs{display:flex}.RelatedContent_wrapper__riU7l{padding-inline:16px}}.BottomRecommendation_root__7aU9w{display:grid;grid-gap:4px;gap:4px;padding-inline:24px}.BottomRecommendation_title__SRj68{font-size:22px;font-weight:600}.BottomRecommendation_count__4HpLo{color:var(--blue-gray-600);font-size:16px;font-weight:400}.Slider_root__c0Jo8{position:relative;display:grid}.Slider_scroller__KHjw4{display:flex;gap:20px;overflow:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain;max-inline-size:100%;min-block-size:100%;touch-action:pan-x;-ms-overflow-style:none;scrollbar-width:none}.Slider_scroller__KHjw4::-webkit-scrollbar{display:none}.Slider_scroller__KHjw4>*{flex-grow:1;flex-shrink:0;scroll-snap-align:start}.Slider_scroller__KHjw4>:last-child{scroll-snap-align:end}.Slider_arrow__8LCca{display:grid;place-content:center;width:36px;height:36px;position:absolute;top:50%;background:var(--white);border:1px solid var(--blue-gray-200);border-radius:100vmax;box-shadow:0 2px 4px 0 rgba(0,0,0,.25);color:var(--blue-gray-800);padding:0;opacity:1;visibility:visible;pointer-events:all;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;transition-delay:0s;cursor:pointer;z-index:1}.Slider_prev__YMssa{left:0;translate:-50% -50%}.Slider_next__fa9IO{right:0;translate:50% -50%}.Slider_hidden__rs7nK{opacity:0;visibility:hidden;pointer-events:none}.BottomRecommendationCard_root__gffTk{inline-size:clamp(220px,12.63rem + 3.45vw,260px);position:relative;border-radius:8px;background-color:#fff;border:1px solid var(--blue-gray-200)}.BottomRecommendationCard_root__gffTk .slideshow-thumbnail{border-radius:0;border-top-left-radius:8px;border-top-right-radius:8px}.BottomRecommendationCard_root__gffTk .slideshow-card-content{padding:16px}.BottomRecommendationCard_root__gffTk .slideshow-author{max-width:50%;word-break:break-all;white-space:break-spaces;-webkit-line-clamp:1;display:-webkit-box;-webkit-box-orient:vertical}.BottomRecommendationCard_root__gffTk .slideshow-stats{margin-block-start:auto}.BottomRecommendationCard_root__gffTk:focus,.BottomRecommendationCard_root__gffTk:focus-visible{outline:none;border-color:var(--celadon-blue);border-width:1px}.BottomRecommendationCard_root__gffTk .BottomRecommendationCard_text__5jKNE{display:-webkit-box;-webkit-box-orient:vertical;white-space:break-spaces;word-break:break-word;-webkit-line-clamp:1}.BottomRecommendationCard_link__pHORq:before{content:"";position:absolute;inset:0;z-index:1}.BottomRecommendationCard_metaLine__shwPk{display:flex;gap:8px;align-items:center;white-space:nowrap;grid-area:c}.ScribdRecommendation_root__t3ezS{display:grid;grid-gap:16px;gap:16px;padding:0 24px}.ScribdRecommendation_header__Jw_M1{display:grid;grid-template-columns:1fr max-content}.ScribdRecommendation_title__JZ5p7{font-size:22px;font-weight:600}.ScribdRecommendation_link__4DVQz{align-self:end;grid-column:2;grid-row:1/span 2;color:var(--celadon-blue-dark)}.ScribdRecommendation_link__4DVQz:hover{color:var(--celadon-blue)}.ScribdRecommendationCard_root__ef2Y_{--ease:cubic-bezier(0.2,0,0,1);--rec-bg-1:#f1e3e3;--rec-bg-2:#f6f4e3;--rec-bg-3:#e1eaec;--rec-bg-4:#efebef;--rec-bg-5:#f1f0f0;--rec-bg-6:#f4eadb;--rec-bg-7:#eaeee7;--rec-bg-8:#e3e8ef;--rec-bg-9:#f1eee6;max-width:172px;position:relative;height:100%;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:max-content minmax(0,1fr);align-content:flex-start;background-color:var(--white);border:1px solid var(--blue-gray-200);box-shadow:none;transition:box-shadow .2s ease-in-out;text-decoration:none}.ScribdRecommendationCard_root__ef2Y_:hover{box-shadow:0 2px 10px rgba(0,0,0,.1)}.ScribdRecommendationCard_thumb__5VVNh{position:relative;display:flex;background-color:var(--blue-gray-200);margin-block-start:24px;margin-inline:20px}.ScribdRecommendationCard_root__ef2Y_:before{position:absolute;inset:0;aspect-ratio:19/16;content:"";background-color:var(--block-color)}.ScribdRecommendationCard_thumb__5VVNh img{--reveal-delay:calc(30ms * var(--card-index));aspect-ratio:inherit;box-shadow:0 4px 6px rgba(0,0,0,.2);-o-object-fit:cover;object-fit:cover;opacity:0;visibility:hidden;transition:opacity .3s var(--ease);transition-delay:var(--reveal-delay)}.ScribdRecommendationCard_loaded__FTN_f img{opacity:1;visibility:visible}.ScribdRecommendationCard_content__ObcvL{display:flex;flex-direction:column;padding-block:20px;padding-inline:16px}.ScribdRecommendationCard_content__ObcvL .rating{padding-block-start:8px;margin-block-start:auto}@media (max-width:928px){.ScribdRecommendationCard_content__ObcvL .rating{flex-direction:column;align-items:flex-start}}.Rating_root__fgZQJ{display:flex;align-items:center;-moz-column-gap:6px;column-gap:6px;white-space:nowrap}.Rating_root__fgZQJ :first-child{color:#e47b01;font-size:16px;line-height:1}.Rating_root__fgZQJ :last-child{color:var(--blue-gray-600);font-size:14px;letter-spacing:-.25px}.Transcript_root__Vrf6Q{width:100%;max-width:var(--max-content-width);display:grid;grid-gap:8px;gap:8px;padding:32px 16px;margin-inline:auto;position:relative;background:#fff}.Transcript_title__YgAka{display:flex;align-items:center;gap:4px;font-weight:300;word-break:break-word}.Transcript_list__faItj{list-style-type:none;padding-inline-start:0;word-break:break-word}.Transcript_link__MLbGS{color:var(--celadon-blue);font-weight:700;line-height:22px;text-decoration:none;cursor:pointer}.EditorsNotes_root__3PcDF{padding:32px 16px;margin:0 auto}.EditorsNotes_heading__XR9E6{font-weight:700;font-size:22px}.EditorsNotes_list__NcG5Y{padding-left:30px;font-size:18px;font-style:italic;color:var(--blue-gray-600)}.EditorsNotes_item__ebBbj{word-break:break-word}@media screen and (min-width:1696px){.EditorsNotes_root__3PcDF{max-width:1688px}}.FixedDownloadButton_root__14xtQ{display:none}@media screen and (max-width:928px){.FixedDownloadButton_root__14xtQ{position:sticky;bottom:0;display:flex;justify-content:right;z-index:3;padding:16px}}.Modal_root__TYkzh[open]{opacity:1;animation:Modal_slide-in__GHXut .3s ease-out}.Modal_root__TYkzh{--max-height:calc(100dvb - var(--header-height));--slide-from:calc(-50% + 8px);--slide-to:-50%;--title-size:80px;max-width:100%;max-height:var(--max-height);top:50%;left:50%;translate:-50% -50%;box-shadow:0 0 0 1px rgba(9,30,66,.08),0 2px 1px rgba(9,30,66,.08),0 0 20px -6px rgba(9,30,66,.3);border:0;border-radius:var(--border-radius);padding:0;opacity:0;animation:Modal_slide-out__m_Ov2 .2s ease-in;transition:display allow-discrete .3s,overlay allow-discrete .3s;overflow:clip}@starting-style{.Modal_root__TYkzh[open]{opacity:0}}.Modal_root__TYkzh.Modal_small__hupRE{width:400px}.Modal_root__TYkzh.Modal_medium__j8NOV{width:600px}.Modal_root__TYkzh.Modal_large__ygVmr{width:800px}.Modal_root__TYkzh.Modal_xlarge__HeXWk{width:960px}.Modal_wrapper__4UTGq{position:relative;display:flex;flex-direction:column}.Modal_wrapper__4UTGq>h1+*{flex:1 1;max-height:calc(var(--max-height) - var(--title-size));overflow:clip auto}@media screen and (max-width:520px){.Modal_root__TYkzh,.Modal_root__TYkzh.Modal_large__ygVmr,.Modal_root__TYkzh.Modal_medium__j8NOV,.Modal_root__TYkzh.Modal_small__hupRE,.Modal_root__TYkzh.Modal_xlarge__HeXWk{width:100vw}}@media screen and (max-width:928px){.Modal_root__TYkzh.Modal_bottomPlacement__BUbfp{--slide-from:8px;--slide-to:0;width:100vw;top:unset;bottom:0;translate:-50% 0;border-bottom-left-radius:0;border-bottom-right-radius:0}}@keyframes Modal_slide-in__GHXut{0%{translate:-50% var(--slide-from);opacity:0}to{translate:-50% var(--slide-to);opacity:1}}@keyframes Modal_slide-out__m_Ov2{0%{translate:-50% var(--slide-to);opacity:1}to{translate:-50% var(--slide-from);opacity:0}}.Modal_root__TYkzh::backdrop{background-color:transparent;transition:display allow-discrete .3s,overlay allow-discrete .3s,background-color .3s}.Modal_root__TYkzh[open]::backdrop{background-color:rgba(0,0,0,.6)}@starting-style{.Modal_root__TYkzh[open]::backdrop{background-color:transparent}}.Modal_title__xhSfl{height:var(--title-size);display:flex;align-items:center;color:var(--blue-gray-900);font-size:20px;font-weight:600;border-bottom:1px solid var(--blue-gray-200);padding-inline-start:20px;padding-inline-end:60px}.Modal_title__xhSfl:first-letter{text-transform:capitalize}.Modal_content__R1F4d{padding-inline:20px;padding-block:24px}.Modal_actions__t63hZ{display:flex;align-items:center;justify-content:flex-end;gap:24px;padding-inline:20px;padding-block:16px}.CloseButton_root__JCTRm{position:absolute;right:16px;top:16px;width:40px;height:40px;display:grid;place-content:center;color:var(--blue-gray-600);border-radius:100vmax;background-color:transparent;border:0;padding:0;margin:0;transition:background-color .2s ease-in-out;cursor:pointer}.CloseButton_root__JCTRm:hover{background-color:rgba(var(--blue-gray-600-rgb),.05)}.ReportForm_root__RNqAc{display:grid;grid-gap:10px;gap:10px}.ReportForm_selectField__Kyyaj{max-width:unset!important}.LikeModal_more__R9uAk{justify-self:center;color:var(--celadon-blue);font-weight:500;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out}.LikeModal_more__R9uAk.LikeModal_visible__t1vr4{opacity:1;visibility:visible;transition-delay:0s}.LikesUserList_root__RMFUk{list-style:none;padding:0 0 16px;margin:0}.LikesUserList_root__RMFUk>li{display:grid}.LikesUserList_root__RMFUk>li:last-child .LikesUserList_link__NeMA0{border-bottom:none}.LikesUserList_link__NeMA0{display:grid;grid-template-columns:repeat(2,max-content) 1fr;grid-template-rows:repeat(2,min-content);grid-template-areas:"avatar username summary" "avatar title title";grid-column-gap:12px;-moz-column-gap:12px;column-gap:12px;grid-row-gap:0;row-gap:0;border-bottom:1px solid var(--blue-gray-200);padding:8px 0;text-decoration:none}.LikesUserList_avatar__VRXz2{grid-area:avatar}.LikesUserList_username__c84om{grid-area:username}.LikesUserList_summary___gbSG{grid-area:summary}.LikesUserList_title__UF0V6{grid-area:title}.ViewModal_content__GPKXy{padding-block:0}.ViewModal_row__xLxnz{display:flex;justify-content:space-between;border-bottom:1px dashed var(--blue-gray-300);color:var(--blue-gray-800);padding:16px 0}.ViewModal_row__xLxnz:last-child{border-bottom:none}.ConfirmRemoveSavedModal_description__2EAEu{color:var(--blue-gray-800);font-weight:400;font-size:16px;line-height:20px;padding:24px 0}div.SaveToNewListModal_input__Fi90k{max-width:unset;margin-bottom:24px}.SaveToNewListModal_checkboxWrapper__y_w0m .SaveToNewListModal_checkboxLabel__lMiU9:hover,.SaveToNewListModal_checkboxWrapper__y_w0m:hover .SaveToNewListModal_checkboxLabel__lMiU9,.SaveToNewListModal_checkboxWrapper__y_w0m:hover .SaveToNewListModal_input__Fi90k{color:var(--blue-gray-800)}.SaveToNewListModal_checkboxWrapper__y_w0m .SaveToNewListModal_checkboxLabel__lMiU9{color:var(--blue-gray-800);font-weight:400;font-size:14px;flex-direction:column;display:flex;line-height:18px}.SaveToNewListModal_imageContainer__Dx4nD{display:flex;flex-direction:column;justify-content:center;align-items:center;width:178px;margin:0 auto 24px}.SaveToNewListModal_imageContainer__Dx4nD img{height:100px}.SaveToNewListModal_errorContainer__FBZPH{margin:16px 0}
Svoboda | Graniru | BBC Russia | Golosameriki | Facebook
SlideShare a Scribd company logo
Clean Code
In
Test Automation
Presented By
Aditya Kumar Singh
Automation Consultant
(Test Automation Competency)
1. Introduction
2. Clean Testing (Arrange -> Act -> Assert)
3. Characteristics
o Of Good Test Automation Code
o Of Bad Test Automation Code
4. Clean Code Principles in Test Automation
5. Best Practices in Test Automation
Introduction
• Writing clean code is paramount for ensuring that tests
are effective, maintainable, and reliable.
• Clean code in test automation not only facilitates easier
understanding and modification by team members but
also enhances the overall quality and performance of the
test suite.
• The focus will be on differentiating between good and
bad practices in test automation, highlighting the
characteristics of well-written tests and common pitfalls to
avoid.
• By examining these distinctions, we aim to promote best
practices that lead to more efficient and robust automated
testing.
Clean Testing
• Clean Testing is a methodology in test automation that emphasizes writing clear,
readable, and maintainable tests by following a structured pattern known as
Arrange-Act-Assert (AAA).
• This pattern ensures that tests are easy to understand and consistently organized,
which helps in identifying and fixing issues quickly.
• Like clean code, a clean test is simple, direct, and not cluttered with unnecessary
steps or information.
Arrange -> Act -> Assert
• Arrange
o In the Arrange phase, you set up everything needed for the test. This includes:
 Initializing Objects: Create instances of the classes you will test.
 Setting Up Data: Prepare any data or state required for the test.
 Mocking Dependencies: Use mocks or stubs for any external dependencies.
@BeforeClass
public void setUp() {
// Arrange
System.setProperty("webdriver.chrome.driver",
"path/to/chromedriver");
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.get("https://example.com/login");
}
Arrange -> Act -> Assert
• Act
o In the Act phase, you perform the action that you want to test.
o This typically involves calling a method or function.
o Or Simply, Choose an action that will trigger the test result – this could be a click, calling a specific function, or something
else.
@Test
public void testLogin() {
// Arrange
WebElement usernameField = driver.findElement(By.id("username"));
WebElement passwordField = driver.findElement(By.id("password"));
WebElement loginButton = driver.findElement(By.id("loginButton"));
// Act
usernameField.sendKeys("testuser");
passwordField.sendKeys("testpassword");
loginButton.click();
}
Arrange -> Act -> Assert
• Assert
o In the Assert phase, you verify that the outcome is as expected.
o This is where you check the results of the action performed in the Act phase against the expected results.
o Or Assert the result was what was expected.
@Test
public void testLogin() {
// Arrange
WebElement usernameField = driver.findElement(By.id("username"));
WebElement passwordField = driver.findElement(By.id("password"));
WebElement loginButton = driver.findElement(By.id("loginButton"));
// Act
usernameField.sendKeys("testuser");
passwordField.sendKeys("testpassword");
loginButton.click();
// Assert
WebElement welcomeMessage = driver.findElement(By.id("welcomeMessage"));
Assert.assertTrue(welcomeMessage.isDisplayed(), "Login failed: Welcome message is not displayed.");
Assert.assertEquals(welcomeMessage.getText(), "Welcome, testuser!", "Login failed: Incorrect welcome message.");
}
Characteristics Of Good Automation Code
Good automation code is essential for ensuring reliability, efficiency, and maintainability in automated processes. Here are
some key characteristics of well-written automation code:
• Readability
• Clear and Concise: The code should be easy to read and understand.
• Consistent Naming Conventions: Adopting a consistent style for naming variables, functions, and classes.
• Comments: Appropriate comments help others (and your future self) understand the code's functionality and intent.
• Modularity
• Functions and Modules: Breaking down the code into reusable functions and modules makes it easier to manage.
• Single Responsibility: Each function or module should have a single, well-defined responsibility.
• Flexibility and Configurability
• Configurable Parameters: Using configuration files or environment variables allows the code to be easily adapted to different
environments or use cases without modifying the codebase.
• Extensibility: The code should be designed to accommodate future changes or additional features with minimal modifications.
• Error Handling and Logging
• Error Handling: Proper error handling mechanisms should be in place to gracefully handle exceptions and errors without
crashing.
• Logging: Implementing logging helps in debugging and provides insights into the code's execution flow.
Characteristics Of Good Automation Code
• Compliance and Standards
• Adherence to Standards: Following industry standards and best practices for coding ensures that the automation code
is reliable and interoperable.
• Code Reviews: Regular code reviews help identify issues early and improve the overall quality of the code.
• Security
• Secure Practices: Following best security practices, such as avoiding hard-coded credentials and using secure
connections, protects against vulnerabilities.
• Input Validation: Validating inputs ensures that the code handles unexpected or malicious data appropriately.
• Scalability
• Efficient Algorithms: Writing efficient algorithms ensures that the code can handle increasing amounts of data or
complexity without significant performance degradation.
• Parallelization: Where possible, enabling parallel execution of tasks can improve performance.
Characteristics Of Bad Automation Code
Bad automation code can lead to inefficiencies, difficulties in maintenance, and potential failures in automated processes.
Here are some characteristics that typically define poor automation code:
• Poor Readability
• Unclear Naming: Using non-descriptive variable and function names that do not convey their purpose.
• Inconsistent Style: Inconsistent naming conventions and coding styles, leading to confusion and difficulty in following the code.
• Lack of Comments: Absence of comments or documentation, making it difficult to understand the code’s intent and functionality.
• Monolithic Structure
• Lack of Modularity: Writing large, monolithic blocks of code without breaking them down into smaller, reusable functions or
modules.
• Multiple Responsibilities: Functions or modules that handle multiple tasks, making them complex and difficult to understand or
reuse.
• Redundancy
• Code Duplication: Repeating the same code in multiple places instead of abstracting common functionality into reusable
components.
• Weak Error Handling
• No Error Handling: Failing to handle potential errors or exceptions, which can cause the automation to crash unexpectedly.
• Poor Logging: Inadequate or absent logging, making it hard to diagnose issues or understand the code’s execution flow.
Characteristics Of Bad Automation Code
• Non-Adherence to Standards
• Ignoring Best Practices: Not following industry best practices and coding standards, leading to lower quality and less
reliable code.
• No Code Reviews: Skipping code reviews, missing out on opportunities to catch issues early and improve code quality.
• Security Vulnerabilities
• Hardcoded Credentials: Storing sensitive information like credentials directly in the code, which can be a major security
risk.
• Lack of Input Validation: Failing to validate inputs, making the code vulnerable to injection attacks and other security
issues.
• Hardcoding and Inflexibility
• Hardcoded Values: Using hardcoded values for configurations, making the code less flexible and harder to adapt to
different environments.
• Non-Configurable: Lack of configurable parameters, requiring code changes for different use cases or environments.
Clean Code Principles in Test Automation
• Single Responsibility Principle
o This principle states that a class or module should have only one reason to change.
o In test automation, this means that each test case or test suite should focus on testing a single piece of functionality.
o It helps in maintaining the tests, as any change in the feature being tested should only require changes in one place.
Clean Code Principles in Test Automation
• Open/Closed Principle
o The Open/Closed Principle suggests that software entities (classes, functions, etc.) should be open for extension but
closed for modification.
o In test automation, this could mean that your test cases should be designed in a way that allows for easy extension
(adding new test cases) without modifying existing ones.
o This could be achieved through proper abstraction and use of design patterns like Page Object Model.
Clean Code Principles in Test Automation
• FIRST Principle
o FIRST stands for Fast, Isolated/Independent, Repeatable, Self-Validating, and Timely.
 Fast: Tests should execute quickly to provide rapid feedback.
 Isolated/Independent: Tests should not depend on each other. Each test should be able to run independently.
 Repeatable: Tests should produce the same result every time they are run.
 Self-Validating: Tests should have a Boolean output. They should pass or fail clearly.
 Timely: Tests should be written timely, ideally before the code they are testing is implemented, following a test-driven development
(TDD) approach.
Clean Code Principles in Test Automation
• Single Level of Abstraction Principle
o This principle states that there should not be multiple levels of abstraction within a function or method.
o In test automation, this means that test methods should have a single level of abstraction, making them easier to read and
understand.
Clean Code Principles in Test Automation
• Dependency Injection Principle
o This principle promotes injecting dependencies into a class rather than creating them internally.
o In test automation, this allows for easier testing by enabling the injection of mock objects or test doubles to isolate the
component under test.
Best Practices in Test Automation
Descriptive
Naming
Lorem Ipsum is
simply dummy text of
the printing.
Lorem Ipsum is
simply dummy text of
the printing.
Small
And
Focused Tests
Page Object
Model
Parameterized
Values
Assertions
Waits
Logging
And
Reporting
Best Practices in Test Automation
• Use Descriptive Naming
o In test automation, Choose descriptive names for classes, methods, and variables that convey their purpose.
o For Example,
public class LoginPageTest {
@Test
public void loginWithValidCredentials_shouldSucceed() {
// Arrange: Set up the test scenario
WebDriver driver = new ChromeDriver();
LoginPage loginPage = new LoginPage(driver);
// Act: Perform the login operation
loginPage.navigate();
loginPage.login(TestConstants.VALID_USERNAME, TestConstants.VALID_PASSWORD);
// Assert: Verify the expected outcome
assertTrue("Login successful", driver.getTitle().contains("Dashboard"));
// Clean up: Close the browser
driver.quit();
}
}
Best Practices in Test Automation
• Keep Tests Small and Focused
o In test automation, each test should focus on testing a single functionality or scenario.
o For Example,
// Validating the login page
@Test
public void loginWithValidCredentials_shouldSucceed() {
// Arrange: Set up the test scenario
WebDriver driver = new ChromeDriver();
LoginPage loginPage = new LoginPage(driver);
// Act: Perform the login operation
loginPage.navigate();
loginPage.login(TestConstants.VALID_USER, TestConstants.VALID_PASS);
// Assert: Verify the expected outcome
assertTrue("Login successful", driver.getTitle().contains("Dashboard"));
// Clean up: Close the browser
driver.quit();
}
// Validating the home page
@Test
public void navigateToHomePage_afterSuccessfulLogin() {
// Arrange: Set up the test scenario
WebDriver driver = new ChromeDriver();
LoginPage loginPage = new LoginPage(driver);
HomePage homePage = new HomePage(driver);
// Act: Perform the login operation
loginPage.navigate();
loginPage.login(TestConstants.VALID_USER, TestConstants.VALID_PASS);
// Act: Navigate to the home page
homePage.navigateToHomePage();
// Assert: Verify the expected outcome
assertTrue("Home page is displayed", homePage.isHomePageDisplayed());
// Clean up: Close the browser
driver.quit();
}
Best Practices in Test Automation
• Use Page Object Model (POM)
o In test automation, encapsulate web elements and actions into Page Objects to promote reusability and
maintainability.
o For Example, import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage {
private WebDriver driver;
private By usernameInput = By.id("username");
private By passwordInput = By.id("password");
private By loginButton = By.id("login");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public void navigate() {
driver.get(TestConstants.BASE_URL + "/login");
}
public void login(String username, String password) {
driver.findElement(usernameInput).sendKeys(username);
driver.findElement(passwordInput).sendKeys(password);
driver.findElement(loginButton).click();
}
}
Best Practices in Test Automation
• Avoid Hardcoding Values
o In test automation, use configuration files or constants to store test data and configuration settings.
o For Example,
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage {
private WebDriver driver;
private By usernameInput = By.id("username");
private By passwordInput = By.id("password");
private By loginButton = By.id("login");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public void navigate() {
driver.get(TestConstants.BASE_URL + "/login");
}
public void login(String username, String password) {
driver.findElement(usernameInput).sendKeys(username);
driver.findElement(passwordInput).sendKeys(password);
driver.findElement(loginButton).click();
}
}
// Example:
public class TestConstants {
public static final String BASE_URL = "https://example.com";
public static final String USERNAME = "testuser";
public static final String PASSWORD = "password123";
}
Best Practices in Test Automation
• Keep Assertions Clear and Concise
o In test automation, use meaningful messages in assertions to understand failures easily.
o For Example,
@Test
public void loginWithValidCredentials_shouldSucceed() {
// Arrange: Set up the test scenario
WebDriver driver = new ChromeDriver();
LoginPage loginPage = new LoginPage(driver);
HomePage homePage = new HomePage(driver);
// Act: Perform the login operation
loginPage.navigate();
loginPage.login(TestConstants.VALID_USER, TestConstants.VALID_PASS);
// Assert: Verify the expected outcome
assertEquals("The page title should be 'Dashboard' after successful login", "Dashboard", driver.getTitle());
// Clean up: Close the browser
driver.quit();
}
// Example:
assertEquals("Login successful", driver.getTitle());
Best Practices in Test Automation
• Handle Waits Properly
o In test automation, use explicit and implicit waits to handle synchronization issues.
o For Example,
@BeforeMethod
public void setUp() {
// Set up the ChromeDriver path
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
// Initialize the ChromeDriver
driver = new ChromeDriver();
// Set implicit wait (applies to all elements)
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
// Initialize WebDriverWait (explicit wait)
wait = new WebDriverWait(driver, 10);
// Navigate to the desired URL
driver.get("https://example.com");
}
// Example:
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("elementId")));
Best Practices in Test Automation
• Parameterize Tests
o In test automation, use parameterization to run tests with different data sets.
o For Example,
@Test(dataProvider = "loginData")
public void loginWithValidCredentials_shouldSucceed(String username, String password) {
// Act: Perform the login operation
loginPage.navigate();
loginPage.login(username, password);
// Assert: Verify the expected outcome
assertEquals(driver.getTitle(), "Dashboard", "The page title should be Dashboard'");
}
@Test(dataProvider = "loginData")
public void navigateToHomePage_afterSuccessfulLogin(String username, String password) {
// Act: Perform the login operation
loginPage.navigate();
loginPage.login(username, password);
// Act: Navigate to the home page
homePage.navigateToHomePage();
// Assert: Verify the expected outcome
assertTrue(homePage.isHomePageDisplayed(), "The home page should be displayed after navigation");
}
// Example:
@DataProvider(name = "loginData")
public Object[][] loginData() {
return new Object[][] {
{"username1", "password1"},
{"username2", "password2"}
};
}
Best Practices in Test Automation
• Implement Logging and Reporting
o In test automation, use logging frameworks like Log4j or SLF4J to log informative messages for debugging.
o Utilize reporting tools like ExtentReports or TestNG reports for generating comprehensive test reports.
o For Example,
@Test(dataProvider = "loginData")
public void loginWithValidCredentials_shouldSucceed(String username, String password) {
test = extent.createTest("loginWithValidCredentials_shouldSucceed with " + username);
logger.info("Starting login test with username: " + username);
test.log(Status.INFO, "Starting login test with username: " + username);
// Act: Perform the login operation
loginPage.navigate();
test.log(Status.INFO, "Navigated to login page");
loginPage.login(username, password);
test.log(Status.INFO, "Performed login with username: " + username);
// Assert: Verify the expected outcome
String expectedTitle = "Dashboard";
String actualTitle = driver.getTitle();
logger.info("Verifying the page title. Expected: " + expectedTitle + ", Actual: " + actualTitle);
test.log(Status.INFO, "Verifying the page title. Expected: " + expectedTitle + ", Actual: " + actualTitle);
}
}
// Example:
Logger logger = Logger.getLogger(LoginPageTest.class.getName());
logger.info("Login test started...");
DEMO
Clean Code in Test Automation  Differentiating Between the Good and the Bad

More Related Content

Clean Code in Test Automation Differentiating Between the Good and the Bad

  • 1. Clean Code In Test Automation Presented By Aditya Kumar Singh Automation Consultant (Test Automation Competency)
  • 2. 1. Introduction 2. Clean Testing (Arrange -> Act -> Assert) 3. Characteristics o Of Good Test Automation Code o Of Bad Test Automation Code 4. Clean Code Principles in Test Automation 5. Best Practices in Test Automation
  • 3. Introduction • Writing clean code is paramount for ensuring that tests are effective, maintainable, and reliable. • Clean code in test automation not only facilitates easier understanding and modification by team members but also enhances the overall quality and performance of the test suite. • The focus will be on differentiating between good and bad practices in test automation, highlighting the characteristics of well-written tests and common pitfalls to avoid. • By examining these distinctions, we aim to promote best practices that lead to more efficient and robust automated testing.
  • 4. Clean Testing • Clean Testing is a methodology in test automation that emphasizes writing clear, readable, and maintainable tests by following a structured pattern known as Arrange-Act-Assert (AAA). • This pattern ensures that tests are easy to understand and consistently organized, which helps in identifying and fixing issues quickly. • Like clean code, a clean test is simple, direct, and not cluttered with unnecessary steps or information.
  • 5. Arrange -> Act -> Assert • Arrange o In the Arrange phase, you set up everything needed for the test. This includes:  Initializing Objects: Create instances of the classes you will test.  Setting Up Data: Prepare any data or state required for the test.  Mocking Dependencies: Use mocks or stubs for any external dependencies. @BeforeClass public void setUp() { // Arrange System.setProperty("webdriver.chrome.driver", "path/to/chromedriver"); driver = new ChromeDriver(); driver.manage().window().maximize(); driver.get("https://example.com/login"); }
  • 6. Arrange -> Act -> Assert • Act o In the Act phase, you perform the action that you want to test. o This typically involves calling a method or function. o Or Simply, Choose an action that will trigger the test result – this could be a click, calling a specific function, or something else. @Test public void testLogin() { // Arrange WebElement usernameField = driver.findElement(By.id("username")); WebElement passwordField = driver.findElement(By.id("password")); WebElement loginButton = driver.findElement(By.id("loginButton")); // Act usernameField.sendKeys("testuser"); passwordField.sendKeys("testpassword"); loginButton.click(); }
  • 7. Arrange -> Act -> Assert • Assert o In the Assert phase, you verify that the outcome is as expected. o This is where you check the results of the action performed in the Act phase against the expected results. o Or Assert the result was what was expected. @Test public void testLogin() { // Arrange WebElement usernameField = driver.findElement(By.id("username")); WebElement passwordField = driver.findElement(By.id("password")); WebElement loginButton = driver.findElement(By.id("loginButton")); // Act usernameField.sendKeys("testuser"); passwordField.sendKeys("testpassword"); loginButton.click(); // Assert WebElement welcomeMessage = driver.findElement(By.id("welcomeMessage")); Assert.assertTrue(welcomeMessage.isDisplayed(), "Login failed: Welcome message is not displayed."); Assert.assertEquals(welcomeMessage.getText(), "Welcome, testuser!", "Login failed: Incorrect welcome message."); }
  • 8. Characteristics Of Good Automation Code Good automation code is essential for ensuring reliability, efficiency, and maintainability in automated processes. Here are some key characteristics of well-written automation code: • Readability • Clear and Concise: The code should be easy to read and understand. • Consistent Naming Conventions: Adopting a consistent style for naming variables, functions, and classes. • Comments: Appropriate comments help others (and your future self) understand the code's functionality and intent. • Modularity • Functions and Modules: Breaking down the code into reusable functions and modules makes it easier to manage. • Single Responsibility: Each function or module should have a single, well-defined responsibility. • Flexibility and Configurability • Configurable Parameters: Using configuration files or environment variables allows the code to be easily adapted to different environments or use cases without modifying the codebase. • Extensibility: The code should be designed to accommodate future changes or additional features with minimal modifications. • Error Handling and Logging • Error Handling: Proper error handling mechanisms should be in place to gracefully handle exceptions and errors without crashing. • Logging: Implementing logging helps in debugging and provides insights into the code's execution flow.
  • 9. Characteristics Of Good Automation Code • Compliance and Standards • Adherence to Standards: Following industry standards and best practices for coding ensures that the automation code is reliable and interoperable. • Code Reviews: Regular code reviews help identify issues early and improve the overall quality of the code. • Security • Secure Practices: Following best security practices, such as avoiding hard-coded credentials and using secure connections, protects against vulnerabilities. • Input Validation: Validating inputs ensures that the code handles unexpected or malicious data appropriately. • Scalability • Efficient Algorithms: Writing efficient algorithms ensures that the code can handle increasing amounts of data or complexity without significant performance degradation. • Parallelization: Where possible, enabling parallel execution of tasks can improve performance.
  • 10. Characteristics Of Bad Automation Code Bad automation code can lead to inefficiencies, difficulties in maintenance, and potential failures in automated processes. Here are some characteristics that typically define poor automation code: • Poor Readability • Unclear Naming: Using non-descriptive variable and function names that do not convey their purpose. • Inconsistent Style: Inconsistent naming conventions and coding styles, leading to confusion and difficulty in following the code. • Lack of Comments: Absence of comments or documentation, making it difficult to understand the code’s intent and functionality. • Monolithic Structure • Lack of Modularity: Writing large, monolithic blocks of code without breaking them down into smaller, reusable functions or modules. • Multiple Responsibilities: Functions or modules that handle multiple tasks, making them complex and difficult to understand or reuse. • Redundancy • Code Duplication: Repeating the same code in multiple places instead of abstracting common functionality into reusable components. • Weak Error Handling • No Error Handling: Failing to handle potential errors or exceptions, which can cause the automation to crash unexpectedly. • Poor Logging: Inadequate or absent logging, making it hard to diagnose issues or understand the code’s execution flow.
  • 11. Characteristics Of Bad Automation Code • Non-Adherence to Standards • Ignoring Best Practices: Not following industry best practices and coding standards, leading to lower quality and less reliable code. • No Code Reviews: Skipping code reviews, missing out on opportunities to catch issues early and improve code quality. • Security Vulnerabilities • Hardcoded Credentials: Storing sensitive information like credentials directly in the code, which can be a major security risk. • Lack of Input Validation: Failing to validate inputs, making the code vulnerable to injection attacks and other security issues. • Hardcoding and Inflexibility • Hardcoded Values: Using hardcoded values for configurations, making the code less flexible and harder to adapt to different environments. • Non-Configurable: Lack of configurable parameters, requiring code changes for different use cases or environments.
  • 12. Clean Code Principles in Test Automation • Single Responsibility Principle o This principle states that a class or module should have only one reason to change. o In test automation, this means that each test case or test suite should focus on testing a single piece of functionality. o It helps in maintaining the tests, as any change in the feature being tested should only require changes in one place.
  • 13. Clean Code Principles in Test Automation • Open/Closed Principle o The Open/Closed Principle suggests that software entities (classes, functions, etc.) should be open for extension but closed for modification. o In test automation, this could mean that your test cases should be designed in a way that allows for easy extension (adding new test cases) without modifying existing ones. o This could be achieved through proper abstraction and use of design patterns like Page Object Model.
  • 14. Clean Code Principles in Test Automation • FIRST Principle o FIRST stands for Fast, Isolated/Independent, Repeatable, Self-Validating, and Timely.  Fast: Tests should execute quickly to provide rapid feedback.  Isolated/Independent: Tests should not depend on each other. Each test should be able to run independently.  Repeatable: Tests should produce the same result every time they are run.  Self-Validating: Tests should have a Boolean output. They should pass or fail clearly.  Timely: Tests should be written timely, ideally before the code they are testing is implemented, following a test-driven development (TDD) approach.
  • 15. Clean Code Principles in Test Automation • Single Level of Abstraction Principle o This principle states that there should not be multiple levels of abstraction within a function or method. o In test automation, this means that test methods should have a single level of abstraction, making them easier to read and understand.
  • 16. Clean Code Principles in Test Automation • Dependency Injection Principle o This principle promotes injecting dependencies into a class rather than creating them internally. o In test automation, this allows for easier testing by enabling the injection of mock objects or test doubles to isolate the component under test.
  • 17. Best Practices in Test Automation Descriptive Naming Lorem Ipsum is simply dummy text of the printing. Lorem Ipsum is simply dummy text of the printing. Small And Focused Tests Page Object Model Parameterized Values Assertions Waits Logging And Reporting
  • 18. Best Practices in Test Automation • Use Descriptive Naming o In test automation, Choose descriptive names for classes, methods, and variables that convey their purpose. o For Example, public class LoginPageTest { @Test public void loginWithValidCredentials_shouldSucceed() { // Arrange: Set up the test scenario WebDriver driver = new ChromeDriver(); LoginPage loginPage = new LoginPage(driver); // Act: Perform the login operation loginPage.navigate(); loginPage.login(TestConstants.VALID_USERNAME, TestConstants.VALID_PASSWORD); // Assert: Verify the expected outcome assertTrue("Login successful", driver.getTitle().contains("Dashboard")); // Clean up: Close the browser driver.quit(); } }
  • 19. Best Practices in Test Automation • Keep Tests Small and Focused o In test automation, each test should focus on testing a single functionality or scenario. o For Example, // Validating the login page @Test public void loginWithValidCredentials_shouldSucceed() { // Arrange: Set up the test scenario WebDriver driver = new ChromeDriver(); LoginPage loginPage = new LoginPage(driver); // Act: Perform the login operation loginPage.navigate(); loginPage.login(TestConstants.VALID_USER, TestConstants.VALID_PASS); // Assert: Verify the expected outcome assertTrue("Login successful", driver.getTitle().contains("Dashboard")); // Clean up: Close the browser driver.quit(); } // Validating the home page @Test public void navigateToHomePage_afterSuccessfulLogin() { // Arrange: Set up the test scenario WebDriver driver = new ChromeDriver(); LoginPage loginPage = new LoginPage(driver); HomePage homePage = new HomePage(driver); // Act: Perform the login operation loginPage.navigate(); loginPage.login(TestConstants.VALID_USER, TestConstants.VALID_PASS); // Act: Navigate to the home page homePage.navigateToHomePage(); // Assert: Verify the expected outcome assertTrue("Home page is displayed", homePage.isHomePageDisplayed()); // Clean up: Close the browser driver.quit(); }
  • 20. Best Practices in Test Automation • Use Page Object Model (POM) o In test automation, encapsulate web elements and actions into Page Objects to promote reusability and maintainability. o For Example, import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; public class LoginPage { private WebDriver driver; private By usernameInput = By.id("username"); private By passwordInput = By.id("password"); private By loginButton = By.id("login"); public LoginPage(WebDriver driver) { this.driver = driver; } public void navigate() { driver.get(TestConstants.BASE_URL + "/login"); } public void login(String username, String password) { driver.findElement(usernameInput).sendKeys(username); driver.findElement(passwordInput).sendKeys(password); driver.findElement(loginButton).click(); } }
  • 21. Best Practices in Test Automation • Avoid Hardcoding Values o In test automation, use configuration files or constants to store test data and configuration settings. o For Example, import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; public class LoginPage { private WebDriver driver; private By usernameInput = By.id("username"); private By passwordInput = By.id("password"); private By loginButton = By.id("login"); public LoginPage(WebDriver driver) { this.driver = driver; } public void navigate() { driver.get(TestConstants.BASE_URL + "/login"); } public void login(String username, String password) { driver.findElement(usernameInput).sendKeys(username); driver.findElement(passwordInput).sendKeys(password); driver.findElement(loginButton).click(); } } // Example: public class TestConstants { public static final String BASE_URL = "https://example.com"; public static final String USERNAME = "testuser"; public static final String PASSWORD = "password123"; }
  • 22. Best Practices in Test Automation • Keep Assertions Clear and Concise o In test automation, use meaningful messages in assertions to understand failures easily. o For Example, @Test public void loginWithValidCredentials_shouldSucceed() { // Arrange: Set up the test scenario WebDriver driver = new ChromeDriver(); LoginPage loginPage = new LoginPage(driver); HomePage homePage = new HomePage(driver); // Act: Perform the login operation loginPage.navigate(); loginPage.login(TestConstants.VALID_USER, TestConstants.VALID_PASS); // Assert: Verify the expected outcome assertEquals("The page title should be 'Dashboard' after successful login", "Dashboard", driver.getTitle()); // Clean up: Close the browser driver.quit(); } // Example: assertEquals("Login successful", driver.getTitle());
  • 23. Best Practices in Test Automation • Handle Waits Properly o In test automation, use explicit and implicit waits to handle synchronization issues. o For Example, @BeforeMethod public void setUp() { // Set up the ChromeDriver path System.setProperty("webdriver.chrome.driver", "path/to/chromedriver"); // Initialize the ChromeDriver driver = new ChromeDriver(); // Set implicit wait (applies to all elements) driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); // Initialize WebDriverWait (explicit wait) wait = new WebDriverWait(driver, 10); // Navigate to the desired URL driver.get("https://example.com"); } // Example: WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("elementId")));
  • 24. Best Practices in Test Automation • Parameterize Tests o In test automation, use parameterization to run tests with different data sets. o For Example, @Test(dataProvider = "loginData") public void loginWithValidCredentials_shouldSucceed(String username, String password) { // Act: Perform the login operation loginPage.navigate(); loginPage.login(username, password); // Assert: Verify the expected outcome assertEquals(driver.getTitle(), "Dashboard", "The page title should be Dashboard'"); } @Test(dataProvider = "loginData") public void navigateToHomePage_afterSuccessfulLogin(String username, String password) { // Act: Perform the login operation loginPage.navigate(); loginPage.login(username, password); // Act: Navigate to the home page homePage.navigateToHomePage(); // Assert: Verify the expected outcome assertTrue(homePage.isHomePageDisplayed(), "The home page should be displayed after navigation"); } // Example: @DataProvider(name = "loginData") public Object[][] loginData() { return new Object[][] { {"username1", "password1"}, {"username2", "password2"} }; }
  • 25. Best Practices in Test Automation • Implement Logging and Reporting o In test automation, use logging frameworks like Log4j or SLF4J to log informative messages for debugging. o Utilize reporting tools like ExtentReports or TestNG reports for generating comprehensive test reports. o For Example, @Test(dataProvider = "loginData") public void loginWithValidCredentials_shouldSucceed(String username, String password) { test = extent.createTest("loginWithValidCredentials_shouldSucceed with " + username); logger.info("Starting login test with username: " + username); test.log(Status.INFO, "Starting login test with username: " + username); // Act: Perform the login operation loginPage.navigate(); test.log(Status.INFO, "Navigated to login page"); loginPage.login(username, password); test.log(Status.INFO, "Performed login with username: " + username); // Assert: Verify the expected outcome String expectedTitle = "Dashboard"; String actualTitle = driver.getTitle(); logger.info("Verifying the page title. Expected: " + expectedTitle + ", Actual: " + actualTitle); test.log(Status.INFO, "Verifying the page title. Expected: " + expectedTitle + ", Actual: " + actualTitle); } } // Example: Logger logger = Logger.getLogger(LoginPageTest.class.getName()); logger.info("Login test started...");
  • 26. DEMO