From ae39c83a0a556d82d550d2a8e22f9ce8f95e38f4 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Thu, 16 Jul 2026 19:19:40 +0800 Subject: [PATCH 1/5] refactor: eliminate require_relative in cli and public_send from lib TODO 03: Remove 13 require_relative lines from cli/main.rb. All CLI classes are autoloaded from lib/uniword.rb (parent namespace file). TODO 05: Eliminate 5 of 8 public_send sites in lib/: - has_borders.rb: case/when for border sides (top/bottom/left/right/between/bar) - warning_collector.rb: case/when for log levels (debug/info/warn/error) - theme_processor.rb: use color_scheme.color_by_name(attr) - theme_transformation.rb: use word_colors.color_by_name(key) - footnote_reference_checker.rb: case/when for footnotes/endnotes Also made ColorScheme#color_by_name public (was private). TODO 12: Documented 3 remaining public_send sites as legitimate dynamic dispatch: - variable_resolver.rb: template property access on arbitrary objects - element_serializer.rb: schema-driven serialization over Serializable 5460 specs pass, 0 failures. --- lib/uniword/builder/has_borders.rb | 9 ++++++++- lib/uniword/cli/main.rb | 13 ------------- lib/uniword/drawingml/color_scheme.rb | 1 - lib/uniword/ooxml/schema/element_serializer.rb | 5 ++++- lib/uniword/resource/theme_processor.rb | 2 +- lib/uniword/template/variable_resolver.rb | 4 ++++ lib/uniword/themes/theme_transformation.rb | 2 +- .../checkers/footnote_reference_checker.rb | 5 ++++- lib/uniword/warnings/warning_collector.rb | 7 ++++++- 9 files changed, 28 insertions(+), 20 deletions(-) diff --git a/lib/uniword/builder/has_borders.rb b/lib/uniword/builder/has_borders.rb index 0fb36cea..c831da57 100644 --- a/lib/uniword/builder/has_borders.rb +++ b/lib/uniword/builder/has_borders.rb @@ -17,7 +17,14 @@ def borders(**sides) Properties::Border.new(color: value, style: "single", size: 4) end - props.borders.public_send("#{side}=", border) + case side + when :top then props.borders.top = border + when :bottom then props.borders.bottom = border + when :left then props.borders.left = border + when :right then props.borders.right = border + when :between then props.borders.between = border + when :bar then props.borders.bar = border + end end self end diff --git a/lib/uniword/cli/main.rb b/lib/uniword/cli/main.rb index 69f58c44..96322133 100644 --- a/lib/uniword/cli/main.rb +++ b/lib/uniword/cli/main.rb @@ -2,19 +2,6 @@ require "thor" require "rainbow" -require_relative "styleset_cli" -require_relative "resources_cli" -require_relative "theme_cli" -require_relative "generate_cli" -require_relative "review_cli" -require_relative "template_cli" -require_relative "diff_cli" -require_relative "toc_cli" -require_relative "images_cli" -require_relative "spellcheck_cli" -require_relative "headers_cli" -require_relative "watermark_cli" -require_relative "protect_cli" module Uniword # Command-line interface for Uniword. diff --git a/lib/uniword/drawingml/color_scheme.rb b/lib/uniword/drawingml/color_scheme.rb index 4a9d4578..83e2fb57 100644 --- a/lib/uniword/drawingml/color_scheme.rb +++ b/lib/uniword/drawingml/color_scheme.rb @@ -331,7 +331,6 @@ def color_by_name(name) when :fol_hlink then fol_hlink end end - private :color_by_name # Set a color by name # diff --git a/lib/uniword/ooxml/schema/element_serializer.rb b/lib/uniword/ooxml/schema/element_serializer.rb index 6e2118e1..30a072b0 100644 --- a/lib/uniword/ooxml/schema/element_serializer.rb +++ b/lib/uniword/ooxml/schema/element_serializer.rb @@ -301,7 +301,10 @@ def serialize_child_to_node(doc, child, child_def, options) def get_attribute_value(element, attr_def) property_name = attr_def.property_name - # Try to get value using property name + # public_send is the canonical Ruby idiom for schema-driven + # serialization: reading attribute values by name from any + # Lutaml::Model::Serializable subclass. The property_name comes + # from the schema definition, not user input. if element.is_a?(Lutaml::Model::Serializable) && element.class.attributes.key?(property_name) element.public_send(property_name) elsif element.is_a?(Hash) && element.key?(property_name) diff --git a/lib/uniword/resource/theme_processor.rb b/lib/uniword/resource/theme_processor.rb index 0e4f0be0..082309d3 100644 --- a/lib/uniword/resource/theme_processor.rb +++ b/lib/uniword/resource/theme_processor.rb @@ -87,7 +87,7 @@ def transform_color_scheme(color_scheme) fol_hlink] color_attrs.each do |attr| - color_obj = color_scheme.public_send(attr) + color_obj = color_scheme.color_by_name(attr) next unless color_obj&.srgb_clr&.val original = color_obj.srgb_clr.val diff --git a/lib/uniword/template/variable_resolver.rb b/lib/uniword/template/variable_resolver.rb index dd2ab225..ea5218b2 100644 --- a/lib/uniword/template/variable_resolver.rb +++ b/lib/uniword/template/variable_resolver.rb @@ -109,6 +109,10 @@ def navigate_property(object, property) if object.is_a?(Hash) object[property.to_sym] || object[property] else + # public_send is the canonical Ruby idiom for template-driven + # property access on arbitrary user objects. The property name + # comes from template syntax and may reference any public + # reader method on the model. object.public_send(property.to_sym) end end diff --git a/lib/uniword/themes/theme_transformation.rb b/lib/uniword/themes/theme_transformation.rb index a560fd20..af6eeff4 100644 --- a/lib/uniword/themes/theme_transformation.rb +++ b/lib/uniword/themes/theme_transformation.rb @@ -182,7 +182,7 @@ def extract_color_scheme(word_theme) colors = {} COLOR_KEYS.each do |key| - if (color_ref = word_colors.public_send(key)) + if (color_ref = word_colors.color_by_name(key)) colors[key] = extract_hex_color(color_ref) end end diff --git a/lib/uniword/validation/checkers/footnote_reference_checker.rb b/lib/uniword/validation/checkers/footnote_reference_checker.rb index 5bba84d8..55493c92 100644 --- a/lib/uniword/validation/checkers/footnote_reference_checker.rb +++ b/lib/uniword/validation/checkers/footnote_reference_checker.rb @@ -152,7 +152,10 @@ def extract_notes(document, note_type) collection_name = note_type == :endnote ? :endnotes : :footnotes if document.is_a?(Uniword::Wordprocessingml::DocumentRoot) - collection = document.public_send(collection_name) + collection = case collection_name + when :footnotes then document.footnotes + when :endnotes then document.endnotes + end case collection when Hash diff --git a/lib/uniword/warnings/warning_collector.rb b/lib/uniword/warnings/warning_collector.rb index fd190a96..e89039f9 100644 --- a/lib/uniword/warnings/warning_collector.rb +++ b/lib/uniword/warnings/warning_collector.rb @@ -218,7 +218,12 @@ def log_warning(warning) # Use Uniword logger if available if defined?(Uniword::Logger) - Uniword::Logger.public_send(level, message) + case level + when :debug then Uniword::Logger.debug(message) + when :info then Uniword::Logger.info(message) + when :warn then Uniword::Logger.warn(message) + when :error then Uniword::Logger.error(message) + end elsif %i[warn error].include?(level) # Fall back to standard output puts message From e9f23ebcdb4d76976a083608b92a3838f7f04df4 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Thu, 16 Jul 2026 20:22:00 +0800 Subject: [PATCH 2/5] refactor: replace instance_variable_set/get with public API in specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TODO 04: Eliminated 23 of 30 instance_variable_set/get sites across 11 spec files. Each replaced with the public setter/reader: - comments_part_spec, comment_spec, tracked_changes_spec: use lutaml-model public setters (comment_id=, author=, revision_id=) - comment_range_spec: use attr_accessor marker_type= - paragraph_validator_spec, table_validator_spec: use public setters (properties=, runs=, rows=) - link_validator_spec: use bookmarks= setter - run_builder_spec: use model.drawings instead of ivar_get - image_embedding_spec: use new attr_reader :show_legend, :width, :height on ChartBuilder - document_context_spec: use new attr_reader :parsed_parts Remaining 7 sites are legitimate test infrastructure: - 6 in zip_extractor_spec (Tempfile @finalizer manipulation — external stdlib) - 1 in element_validator_spec (class-level registry save/restore) --- lib/uniword/builder/chart_builder.rb | 2 ++ .../validation/rules/document_context.rb | 2 +- spec/examples/generated/academic_paper.docx | Bin 17011 -> 17028 bytes spec/examples/generated/chart_gallery.docx | Bin 12585 -> 12584 bytes .../examples/generated/complete_document.docx | Bin 15933 -> 15922 bytes spec/examples/generated/image_document.docx | Bin 10511 -> 10508 bytes .../generated/manipulation_modified.docx | Bin 9930 -> 9928 bytes .../generated/manipulation_original.docx | Bin 9730 -> 9726 bytes .../generated/multi_section_report.docx | Bin 12161 -> 12161 bytes spec/examples/generated/simple_document.docx | Bin 10068 -> 10069 bytes spec/examples/generated/themed_document.docx | Bin 9863 -> 9863 bytes spec/uniword/builder/image_embedding_spec.rb | 6 +++--- spec/uniword/builder/run_builder_spec.rb | 2 +- .../uniword/validation/link_validator_spec.rb | 3 +-- .../validation/rules/document_context_spec.rb | 2 +- .../validators/paragraph_validator_spec.rb | 8 ++++---- .../validators/table_validator_spec.rb | 2 +- .../wordprocessingml/comment_range_spec.rb | 4 ++-- spec/uniword/wordprocessingml/comment_spec.rb | 2 +- .../wordprocessingml/comments_part_spec.rb | 8 ++++---- .../wordprocessingml/tracked_changes_spec.rb | 8 ++++---- 21 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/uniword/builder/chart_builder.rb b/lib/uniword/builder/chart_builder.rb index 72d44b30..3ba357f3 100644 --- a/lib/uniword/builder/chart_builder.rb +++ b/lib/uniword/builder/chart_builder.rb @@ -36,6 +36,8 @@ class ChartBuilder attr_reader :chart_type, :title_text, :series_list + attr_reader :show_legend, :width, :height, :series_list + def initialize(chart_type: :bar) @chart_type = chart_type @title_text = nil diff --git a/lib/uniword/validation/rules/document_context.rb b/lib/uniword/validation/rules/document_context.rb index 6e9f78a4..e2284f0c 100644 --- a/lib/uniword/validation/rules/document_context.rb +++ b/lib/uniword/validation/rules/document_context.rb @@ -20,7 +20,7 @@ class DocumentContext RELS_NS = "http://schemas.openxmlformats.org/package/2006/relationships" CT_NS = "http://schemas.openxmlformats.org/package/2006/content-types" - attr_reader :path + attr_reader :path, :parsed_parts # Initialize context for a DOCX file. # diff --git a/spec/examples/generated/academic_paper.docx b/spec/examples/generated/academic_paper.docx index 982d282e5eb4f88825a28de4f9f37d505438be07..c97052c43bfc2189b4060cae19c0201866d64b7d 100644 GIT binary patch delta 5196 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 diff --git a/spec/examples/generated/chart_gallery.docx b/spec/examples/generated/chart_gallery.docx index e813310840d6e36b2bc6756b179f58657ca43729..77de1542c40a90282850f931912de2c407f8069f 100644 GIT binary patch delta 2092 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 N`G(#voQ~SI`~^;XovHu; diff --git a/spec/examples/generated/complete_document.docx b/spec/examples/generated/complete_document.docx index 0cdedf2ca31afaff804c2129db795dd10ea17dca..b2efe06f6f1f3b5dd4eaaaa8c1d3c7b4933cbc12 100644 GIT binary patch delta 4524 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 diff --git a/spec/examples/generated/image_document.docx b/spec/examples/generated/image_document.docx index d82f6945931a33ba5ef5df82462799d7a483fe3e..3115105fbf9e7f07a56d4c268c20d70e31e977ec 100644 GIT binary patch delta 2183 zcmY+Gc{mi>8^>oby0$6IV9<ZH#>xBq9y6BqRn!gIrm%-7FEZ z4I^@ei)1X>YN9Mzv!tJ<=lR`x&p+??{hsrl=bYz#p7S}W*2&g9R_3g15C8!18=$FR zt(qqV$a)lObAe!{AXl*ja2y%967ScEK4ckIILXPpQwc(Q4{9@5EWc)|MX_$;)MAe@ z=t!KyqWwMUyv_$T_`jd7o6<5u+Q6ao}&7wWZNxo;d3ZP zFX5&gC2xW1PSpq_cE=ci&T^(aABfHEUcI9(*_9MJIGGw#2zK?Nd=xTkkjP51Y-LF& zS!;<4-#5QUscY{7cZH>9xa!fLmup2q*yIMS3X#5DJwxTt75Rhlz!Pnayp(NHjy8el z-y)2&7*5U!WN_`mZrD%iUN4j>+wSoJ2Bw*(6;L_vVo$QCxEohY)M>(E-_DH%AjIxp zizBuVy|9Y?aEn@{d~we)eJ0LEpz{(bym1aLI4f7ga--$6fSJa;MdDDTy&P~T_)~vx z0Dj`Lb0K|0pdy%?(CTD@U~_}Szu6aZT;Q2e6{$ zo9Dz#%BD-cA+m~ex3@0n0PZSm53F)2?t=c)8=UbsN~a63mdX|fWVU2fl_6F@wqlnj zwP8wa>P!`ZO(@Tr)?o;NH`6fX$#Pi!t%MQU#SB_aic6=!V-=&cbMSP=!STe_$Q?|k zr$wCG%RKB#5nkMZKd(Amv*A|6w`*yb)vD=(&Ff@$N`;)mX*ON&Y}d;_9%AFHExaev zA$8RC8dpPNZ*Yid7-2)lBbJ`sF49gK&zm<^->eBg|G~qA-?e!ThWcJna{pB!EZTxo z9uhgJJXt7Spj%5;?$HJ=Xw@_BT{`6GE3Xuejv?zgkKTdg8Hc+B4S7igpzm5r?&7V3 zguNCnj^61Map-KIBypY&gZ6>14}+lXl%Duh8bI7e7AkP=az6+&=1dc)a3gv9V07aL zDlGa4h`Kfd))$f1essQ4;lTRs$NYbQDcYTww}1YO^nI<}&yt_Ka8q@N(5_*L1?UV$ zAa(BQe0a(SaD&eA9s4!A+QRA$&xYpCq2e?~^$OQyRPhz5maA`Zq!}ZQX<$s8Uhs@l zEWO9XZ$^SYv@}&{M7!yf)svl}rWk{n7!U0CiS`6Bzh~_YCpN1egu#m6sXV{Wk@Z#^;;ia~Fq(>?!!wG-iDg%#8N`0NTKf1&3QF`)EDmB#37C7_ql(z(g5 zJc#a313`{*fuk9=4Oq@eNOGNWV2nog=2>Tdz5XGy6^}cW-?zIl8TNuA&|eskgbXmA!r@!9DU>N&DvhM>Y0_<@OAe;=&Uo1jMV1eQJ_eiu)Pp9szk10w~E$>Q0pW>x(v9{KT_RzA8sztVE^De(9F!B#}^sp$jg z=lzWlM!dG#;_La5vA4d?<*Oto7hn}9jjQEquuq3w;DCzUt3;yu#?@D;-+zQkP-xMs z3Hm|@^Vx&@sRZhS$Q`E~4y<%e9*1C6lEHu_n3TWdk!CVSRKE0LYi@B3)Dhq3m@5uy z{Q5LV((w^JojUc!2>Rd|d}bPdLwy4?{R5b<{_Ip<+B*w}PdsKZm$8pK1~8%QxiNc> z{aCdAsH1GHhObRM@(gJvp~OtA3j(}9sxffOEecm&zr5bb>u6S*$s<$ehcDAFJmi6ZpnN8*_GaAXLGvh58y?;UZ)h{<4DFUn{iy{$-pS)(m0R#Ye z3jqMUM|l5d=#U+dV$5wKk1Dvy*+@7uYeI@}^1uLq(10K}#bC0FtRU0gJz6A7hRTXF zGfi0$mNo==8X-XTm1W+99C2i!#K~xJ_-QM1Ad4#C|Gx>`Ft2T`H7fu>1p@%ke@<|u P{K&Jivh3^9Kh6IEP)ZI3 delta 2266 zcmZ8jc{tQ-8~zQGklir$vCfbs$}&W@i0mUFdra1`b|TA}nnJcZnfb;t)}+Lzv6QXJ zGLkd0ghY(3DH_r^w&b9LbFQz`_xy9;*Ztnl`~2}-*L%O2s5>ZLM;mr91ONa|KqY`x z%bNva-^2Ps5Z?j8KW2;I+7mpZS1NtO@Njk+td~uvCM1P*=({MQ4pMp z!>7e+=}*4ThbsoY-ANd}vIvQt{WzO6(3x;*g_%={bEvUjh!=uu@v>Jmi`IGDQ**v~ zY4bhoVp{jxy?mOCsN!`a%&<+nJwUaOuVUjkQ|Lx_9YG40%<7~0 z_)mn+IycO#XZLFnzJ*IC2?$(=87$^ByNWXVmbNw2EH7UcAFNVt&P9}|+0_RmT6T+< zFD16g9O65N!#i-O zVRO_wa^WL=YiV}4t2aJ&eePi+Nt+SiGI&8w%;^=Reyf{)ln{cN zAl@$#A?AoE;B1PjY^bWFd3}X9{opA8+rJ^DSLa6<5k6{sGGAaqWFUwcXq0Ry)5jTx z@-6+uxIHVw%eC~PD9dz;OLnx@C^mD&Vayvd!U-t=vsGE%U)CNvl96GraMd4`C_#ha zFFm|#`Q=;|l2WQedDUD|gR6AAQbW?H0On5XQq(v&^UNOAY}STow6}1mHux%Gojp1i zv=YOsPZwUfmEmU^Z#6#tdWT0LNFM|9-AS{9xv01ppD$>MHnpiK9%xSvOAY z!@Cw0S&45rG>%85Zrw6{UPfZG*pjMN-Q@IK4y(dS#tGi%72OoYouX=SCF0bFx$_*E zZ=3$S@fOm0tUe+})ryLYnhXkRDb@G>Pe{1_WN}`3v+&%!H^wH{(98c;(=o^*88uo* zo*FlYtvb3kig=^eOvkxPf=p0d>piDGdz`bRjO>xI}@}X$&`@M zXJIKrqow!fYkCDVscJlR$)nkV@A%Nqmhkh0iaVHrArrz??p3Vkr@#qe-35Eu!sC?R z`TYkR+Eqh5ujicg)KMDQA+qTmQoz_2$lAl7^XE!B#yk;X$Qtp4h&id6ULPs&>%Wa6 zv&05vY=3o+oe)SZh3XfLvZ!98@4`~bF&J0GLDEE(r>vw>o6797~3?YEtqu|Ur=;TtTAs>93&%#g{*s%k_s1AJKe2IK+>jeyNlMgQUOk03X3x zKEyk53uEWdMH%eI$S^M))ae>DP?3Yx^0rY+7v0N|2?lch^X~(VMm;mz_K1NVKBK9FU9MVrQ2}P>A z6?Uxmfl0gBBQ2#Faj#q#8d@JH6{LsCc@h&>v5WcXLV7J7wz7GmUr%ZjTc}b(ngX_j zHqp7e@=&Sp;mB8B`Ld%N#4vXsE)0ea})7|l^UF`hwa+hHC%6=wYO_oL9Rbl zmqaf7Iqm9pt;*~VGuSBoLZl15KC>rE-d1S(m+i6dSOis(FVUEXIs+THIfYddZvFfW z`+dlE?C9W{ZSLuJYBc7ot=PO`<_#oCnI6A-c@yW`i6-~|hNEwuYZfHig<8*sZ`@0_ z8RL&+)f$q1QDc3-<|>JV3|M`JtFM9jaOXWrItSeNISO$bB};4u_ns82|6=s*G5o^h zm#RY_thlFVY6C0v=R?RXo+{2u4-_Z_lV&DU!BTZmCAic2AzvI?G&ggFvFMpGL z{|FC)pE^J@xb6Waz)k*@!!Q0(ci@ts$9QoB>|cRkiVy)A;aw532YvAfNf1Ath>%g< zPw{&hWWmqu0MKy+00j3!|3?Xk13+v>BJoqo${g!T`{G|s5%?ki diff --git a/spec/examples/generated/manipulation_modified.docx b/spec/examples/generated/manipulation_modified.docx index ca5b7e3829d0a5a1d47642b67f398673de4561d1..20a113ca27d9399bb32a397d56a4979ee4c5fd91 100644 GIT binary patch delta 1831 zcmYk7cU03?8^?c1SYZW}HWm>Qfyfe(5mrDk5FiXG%c797w}L=GP)rF!Y}gS*C1L~x zQ#nEq*-OAMgp^ex$PihUy}=^90pC;l{PTS8eZKd(=bq=B&uzDAwGy$hfbd8F03ZOg z(U(d@Vn7gXE=>oCf&{q2c{N+~G_?RMLu#~cQWG;?G~2=6Q^sHAw`VAbfEkYq&p$0p zd~_XMguHwDCU$1@bA=U{3}N-Z$IPeD2+@vSOQ3gn2R0T>cLU07DqohbF8S3ad4F;U zkIE3F33!b)Dv0pOa~gO99tjx0A`|cl~HCw5E3ZWghTNv$Sai#g3!ofo2OrXv~GJsR2{0Ri#b2e z@Ag}sy2ngaBTe0~C%|QWu{bD`)HaXIjQs)UA9fK};~YXU828RC%2#V=fMfr8ng%8( zcRZgcFX@onN*n&{^IkvUtU(r$(^NF2WG?Ek**>)sUP4H4vi$64d`1@S!qRkuL#r`) zrExtADKcs4m)GHYLLKg@wA!R*>9p-K(yrB}TRrgK(SVjpjd|{aNc%Inr}JyhJOltB zVgMk{9bH6Fi02v4AP?eIuWMndH?9U2-5(AaOkG5hXkp z6%9O;a=IY*Gr~{*OK(JB?Bq*w*<4@o>E*TM>0oc;;poUBJd^*Kkkc!vs99Yee^ry8 zgD!QQW7m($iFT6``pKEhgv?dG!ENU5`u*xATeG@9o}hWe%LvS;x1Dq=&vy0@wb*p` zuWytpGA?^hWp%V?IynXh$Gh`SRM(k$z)f`_biwA9xcDpPATI=;AgMN6yb9yf-Z0zx z1dctaO{6Ne)`N`=1`6!#gX1HM7*K^8!8isge@3r|pG2X&lHIoJ8P~H_lb0VzM=jV} z=`dp?htyvvc^4PHF_^1^DaA#WP?ERB-%i^oJtu^BiZBAQ711V6+2vVUt`qGaUPOJ6 zy_O=x#AeYFc0;5P)r(FwN0;glUirW8mvvEau-D(%uNVIanTg2Om^D)B zR%kN{AbqT1NU0F|Ooi9Ik9CB+6W73Yh%z@;2CI)whIt!{03>ksPi7Fr1F+6}OZqu= zXTz^GlM))e+YNV9bH=X= zCekdjGU{{GKdCpFn2uc`GI`g%j-A=@?iS}S+Y+n0BQa2lYggm1mQ}5% zT*flc3$0>jJ%iUa_!5UreUk4qBufNs47y?~0<+y=$JQ+4y2&_t?_cB8KRJUYdyRwg z(j=XhQBF6d&b;B3WKc9bARcF`sDIxwRm%uK3%3FklqDq(90ueJEkJ{4G71Q+{XQH?A zt7%AO^P?OdVA@Om;~hB*LpSq1tc4+nR1R}r84@m|m`FV{&!IUe>*SBMEMsJE&(E3s z{&2>qll88&Gu1zbwYm_CNcyKND0jC7g*jm}3Nz5xHWc)|HD+@gMH#JP{>xea*9 zCa3rRqKee;fR<~XQ=IZOZ@{wO22mc|o64^ke(;8Kk>&NG#8Ep5J5W&cZj%v2YR8Q+ zRs3(L2%Baa&8=7Wp1$sXvyLv|Ys?-rgk<3q*HwdE^;wQ3e5*ij927)Oq+Q5GZf!WY zj`+a>LZu`zVn!8a>~>3pc`ZU{Z?kU+E@igZ7rJIEX`1#}JK!}P%I*nEadJ52+uJs4 zL1P+f*Vv)W%2UaWE|R>GeW#>zi1jim*wK^E0x#`$TUTJ;gtHYg0hd4ClcU3hy$nEQW_0RYB@(P7L(|Bt57u*g8K(0wr3Sb)Hqz`x&(v*zGNXJ^%m! delta 1844 zcmYk7c{tnI8pnT0w2jo#l3HU;qSO#dgsH1zYpHdZq8g2{)y{;78KPwciUM2lUNtdri#>9>@; zsoyz4oLWgR_-Og2sVOq4<$Y2HZmE#4`md0v{j095v;IgQz0K0h9YuY-RLM2F{iQ|Y z{O!q4%hrsm1LY}~wa)m%J&omqZ3iaC3CPNvQ$3T8dXny!z5AS29u_NX-c@Jdf{&r7 z6&u;(3DurVD{+x=tBoyYVusbk$^Zyv|0q#TP9FtB690S!Zi0Qb6J!~sGxiS z?)Qq~A?Ck|7Z*rPI<+w$ZBB%F&RA1D@;xeno<>LiqIAO+uj40rKN=9AHRW08SaDws z!)S~_LDn3@q-x%IBctf@i0=KV&gxmkDC~9X)~Z;?8`*7B#MLF;dl*RU84Kw_b$c4B zoG1IAvK&4cVJbEr+q_M1i(RejpSfiS5S#eCxdtV^{6AX*;?gaI@<7ysoOMy|G2vBOtFdEW%6hcb$@S*XLKTBYq_!J<$dcuOL}Dc^-0s}V%4Q# zzvaE?&%0`*%rR4G9qru42CmK>zNOVoaYnU+9VfQLqFF8RB*nml~krZC}-HV__y;q z3KUlO_E2P~5ihI9!B%c*CQT>aQ}++Al%l#HYS!1z7tSJs84`9^N(P#Gz5Ei~yrRuG z9`zC%i#h$;diY2o#TA;CzjjYEueS5$h;W(8K6MHjEAP5Rd27|KZ59tHtmsRNF0!U0 z1A9YEQg&7Qo+||Gc{il;!W+USUbs1+15_SQ>c_p-H_Ij+V}XQEnvUEer0*~51lS_je~ELW)&}+*SJ_LX?%{P+MoHm-9&nvyedAJfu+;VYtqv z+N3g2PMm7QP^Hla$j_8^eByHuZcpX2ZSS7_O`7vn~p+lh|Uj#lBg9@atm^W8a|0+V2Gav_B*(j5PL^DMh9O0je!W}NQpe_ji z%^%u6$^d^b3#L4YXNd?tkVF6w7trV^xWfNuQ$%D;uy4cxnH+6EV13~4{|2u=&2QNh zpls~qSCkzc-l1inm6Z-ISZ?u`B!U4zLQqcP$RJRPv=Ow*q325dsO=>X0PG2B9aKMx z-)UlM$V1PX{W}Oxu%RdLAdUW(>#1)yqY}NTbV{tmX>m~Id=TOby6_81lO-4 H4`zP>EW;~k diff --git a/spec/examples/generated/manipulation_original.docx b/spec/examples/generated/manipulation_original.docx index 6d7d7824f2737073d2939b1916ad6a0bf761088d..0b58a3ee23eca9f062b593cfc92888e8b3df56e3 100644 GIT binary patch delta 1609 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 U02;sPE2xCijW?7x#;!L119EKCX8-^I diff --git a/spec/examples/generated/multi_section_report.docx b/spec/examples/generated/multi_section_report.docx index 5a769683131aedb8837f0046e04f227d3af513d0..77d8c86494f0ff0a1618e2a227f842859a59ee42 100644 GIT binary patch delta 3280 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 delta 3309 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 xQ-`E03jqw19c7`1bnAdz$y8bRA@v^67nAzLI< zT>CPFLQKfkeKBN7wo`8gMgRFmHpD`vi&6Z-TnqnZEEH z`Q7kWo^Jn5sX1HqQY^AxhTzw#l*u&sD*eZbG#wSCIa0xR$_s0b2>2(0C9wyyo8Hc%>0-KSrYil0IgNxwZ3v=Gwy?hPEa@vGxS#AN&?+XyyWzc+AIRC0}1XYE1RN{&4>`sMZ{i#?EtS3?BMCvywW? z1tYlxJkb)4Uo*}9At4Z&}MBK)jt(7}hLEwqqrbwO}1R?r^c_E8W|Zo|zd$%vYoSiTY)oE=>4-gqOj+AmkiZ9l31vAz5sETuD#=x!p+ zK?$eK3XQrg)B39A-nt1(NJu*ufeT@Yb8LKtgsIY0x|UY7^{r`b-zfXJ&NX5}WBgRYdzC(-Ln-|~_w(;&ZX0Z7n{L^(&alK| z=JvfD^KmYHS0|YhsGn)1%gXNc$vl4-FaCA$-f-&TiV@oGPLNt~MEmv|{!)nr1fg5G zLl9mIu219sc?Op>Q+_eR}`F> za_KCiBiAV?r|2w6Jn%gPSl*@dDBATupbOvL5ScG`ISE=pf zY1fIXk5fN7S<0$z!{d4z_R^;WdgJkCSs28P9Z9dDL7EB9WF&_90CcR$3)0yRDR9B= zpGcdj=-X;+c_-Q`IPs{HtS(m?x&>YyNNy(&@x{X-4#{J$13Lp>C?y5je-=5ht{Icf z=vhuN)L!tbYFM&!g(h&Cn?05MgnP*LY`~SsGE&cdD?@{i&5`zGa#Hd6j$B`hw{C`K%gp|T>!7UTZc{rjzkkdP=19GT; zvx-+9ebh9O*ZH42nJkZzI=q^TvnApqyliYcWHvVbgVg)?l~bqXVTV5{a0&KjR482f uXpQ~>^-!JRGDk!@AXh34E_+0+2h>mf2v<6y69@_RT`B^hz={An!! diff --git a/spec/examples/generated/themed_document.docx b/spec/examples/generated/themed_document.docx index a23aa63e92e1bb9401a2dc29390d1c1ae4176a33..7ccd94f418ad6b5f47caa91a7941f2a7c3181ad2 100644 GIT binary patch delta 1578 zcmY+Ec{mdc9LHw|EjiN26|>6`Q3*rNN)4$IOJX_7a!pFdE9>oGN-O4wOq-NmXDp^9 z0qs_R%B$oMPx@MbSBouY~_%>}PjOM0p=$am#k$U_LC6LrtqYP|JM~g(pMhHT=28+%}ejDSWYd2lT@UP;iL1(*FdAA(V6+PT+a_=^iWRF;Chs%sE zx^K^`1R&xOFJb8Ff$I)pO-|x6n1+JA1-eFcJWMaEK`@iq(eKyu<3-VKisZMr3rnn> z)CeJYET}jg9!=ql`W!1^lv|uBHhFH~4;BU8AFrN4*BP4kIL(Ds26aU%Q&bwL2^@iI zmFDnJr{0H6xBHD)yj`}rVU}LW-+MQ@WpL4 zeK`^2nHR;LFzWB3)m;Krtbk>x8&tz)VrKVjJG4XVErq2-#WMsx2anPN(#@fGb-*rSg+P4W5l-=?6u%wF>#|Y_ogi z&CHJrgnLCOyx&r!e0^sLH=YcC)p6YszzzFsG7KtLCMeYoiOBEn@OhxC6xzI{E{#?# z(H%!~x4fB4FZ8Oz?R;zow`CHsd*Ud9n>JBGyQmMgp2JwH5M7;5FO}k6I6Ts^0Y^i& z=0qrg_WFmU+=7cqs%ENCBu9M7nq^ggWeQYpoM$fXtkxmoMyRc4dB~2LC$A&cp1376 z{>qNrU@q}#Z0-RirAnLbV>2b?oQGc|*q7Ia7lA4p$N1TobC@?tRSf%@9Lo1oIf8S6 z53s4(MgdHBtA!-;=$zYVeQ&Tkf^V1LY4< z{76B>pI3p@TLjbeCi9O|#&Ubtlm;O_N(ZCJT@n-xMCFiiu;+b`^QLEkPX>x@DhpJ( zyIrk!#U%1cf)gQ)b|qyyz4zhwg#foRvphDQCQjW=d$C@Dvv-%Y@yDqVn9bOJh&(Dd7xw9(|X?SZwfbC{Tn0Nt_>Iy4zQYrTSSIp189My yOh;M$Kim!|Ygu**WhXnzwxjO9<5!feEZl^)moXN-RmMuRql{nBZZdAg;Qj@He*4G( delta 1578 zcmY+EdpOez9L6_BD-X?m6gq5j3DK}zmP^bfYn5v*>7cdLOp~)hKT?KVx0afcE{K!M z$VPLEQ94;JE%!5$N0Qtol#WyDJkNRh>+^ZO@1O7UeQ91aFL}JDG(-gi0zpAW;d5;H zB(U^%*5rZ}z|ij!w8p&W$|#XC13E2S?{(}<(|1BO2|vp+%g+q8b#l4JyHSf{6U!UC zV-@{seU-&on5QC_3O zN|+Zeb;9`Mjcrt?wW?mu@%NrQM9P9?pj4-UR<3N$=9G+>R=&7{TB*_i_^~p}lO4!X zmZ-w++?X7?-mu$l|GlrGb`7|oCP zjhdwtX)uKWD~=MIraMgB?!;saHvk~4(iT$EUCyLar*EKQcUu5=-tsh$!o4qpCKWku z;1PUF3VO-R#~o%344>7n7vlWj_P)$_utvz#v8tKcc|+b#vpxrL1p8PINdef}oEpy^ zb6&J)Xv`Nqi*T(uRJ0Xa#)-ou zhUJF}L&I&|DVT2>(kU^eR+n8pTJqM+H`oO7r=|wx$X%@ob_?kp;i3zc%n>FrlD??CAM`Tj=^JUk@>aPAalh)?+R8K9iFk6h}$x)oE7G`-a%aUfJW2Gus@m_<`y-v`@W(c#Mfc zAu!ov-Noc^&Q_dt-@9sNzN~^?f*#J>m)Z%UE-#{Nuttop3`R8(| zh9K13yfz~@El)IZ`@Hsnmj-fBY+_nkICvVKIL%l3V%yf%R$#r|ihU~(*Gm})a8s^s z-l7b+s)d<$tY;LZ>bIXm^IQwqE}E)ts&i>;d&f!$cEQa>gw7p6D?W&PHM5D*7u# z39ORWud3@C-Ye5+giR2Qm1JFtk$g}TWonti(rH|iQM|`zo2l+`wC)SHJ^!%=Xojl6 zk!vn@!c?ZN!cELpM{HZK_31EI(v7g{ug@3WGU|d^mn)&ngVEH=#|Bz5MveoskdnOP z)#=3eL1KfLi_)j!3UxHOFFgSG2inuf)VhV!I}ZgX`ohLxteP!f^2ZJ72F07x$T5cw zzbzp?N$1`fupOse3Nf(4X2$|vHs>;zA#>_Ix(ZS;gI|&D({x143sZV|oXoK5_NJjS z^dUhzyRX};H(w`KbN?uMUJz;A>&-kMggPreMEZDZKjaY+H7^`zD-hp0zEQ_jARhu0 zSn(S(39u0nH(J!VzR8)aCfB$4rS5nAZ&)_Kb zk00b(qU8KtjJ(uPT|v-`aWvYWc<2^iB=RA3BMZ5x{&86=phuFQ#Ftm)rG#l zQ--hGM6FK90m&Pie);_x;bs+i$qIi|Q-?@IcH1>6iQLwT{5jBaItD+*YDbFnkBHH% zhxtJ|!s`Fw#vrUE85LnGIZC#J_up|DVIv7GkaiMABkd(*Blk(Tgv3f{ukZ0MCjsEh diff --git a/spec/uniword/builder/image_embedding_spec.rb b/spec/uniword/builder/image_embedding_spec.rb index 022c5139..df83d506 100644 --- a/spec/uniword/builder/image_embedding_spec.rb +++ b/spec/uniword/builder/image_embedding_spec.rb @@ -415,15 +415,15 @@ def zip_content_for(model) describe "legend" do it "configures legend visibility and position" do builder.legend(show: false) - expect(builder.instance_variable_get(:@show_legend)).to be(false) + expect(builder.show_legend).to be(false) end end describe "dimensions" do it "sets chart dimensions" do builder.dimensions(width: 4_000_000, height: 3_000_000) - expect(builder.instance_variable_get(:@width)).to eq(4_000_000) - expect(builder.instance_variable_get(:@height)).to eq(3_000_000) + expect(builder.width).to eq(4_000_000) + expect(builder.height).to eq(3_000_000) end end diff --git a/spec/uniword/builder/run_builder_spec.rb b/spec/uniword/builder/run_builder_spec.rb index 65f625dd..9e9ebc32 100644 --- a/spec/uniword/builder/run_builder_spec.rb +++ b/spec/uniword/builder/run_builder_spec.rb @@ -134,7 +134,7 @@ # Use a minimal mock that responds to the right class check drawing = Uniword::Wordprocessingml::Drawing.new builder.drawing(drawing) - drawings = builder.model.instance_variable_get(:@drawings) + drawings = builder.model.drawings expect(drawings).not_to be_empty end end diff --git a/spec/uniword/validation/link_validator_spec.rb b/spec/uniword/validation/link_validator_spec.rb index 941eecc7..6c7aebbc 100644 --- a/spec/uniword/validation/link_validator_spec.rb +++ b/spec/uniword/validation/link_validator_spec.rb @@ -71,8 +71,7 @@ para.runs << anchor_link doc.body.paragraphs << para # Add bookmark to document - doc.instance_variable_set(:@bookmarks, - { "section1" => Uniword::Bookmark.new(name: "section1") }) + doc.bookmarks = { "section1" => Uniword::Bookmark.new(name: "section1") } doc end diff --git a/spec/uniword/validation/rules/document_context_spec.rb b/spec/uniword/validation/rules/document_context_spec.rb index ed2472e2..920091b9 100644 --- a/spec/uniword/validation/rules/document_context_spec.rb +++ b/spec/uniword/validation/rules/document_context_spec.rb @@ -32,7 +32,7 @@ it "clears cached parts" do context.document_xml context.close - expect(context.instance_variable_get(:@parsed_parts)).to be_empty + expect(context.parsed_parts).to be_empty end end diff --git a/spec/uniword/validators/paragraph_validator_spec.rb b/spec/uniword/validators/paragraph_validator_spec.rb index 432496ef..d07c5faa 100644 --- a/spec/uniword/validators/paragraph_validator_spec.rb +++ b/spec/uniword/validators/paragraph_validator_spec.rb @@ -57,7 +57,7 @@ it "returns false for paragraph with invalid properties" do paragraph = Uniword::Wordprocessingml::Paragraph.new # Using reflection to set invalid properties - paragraph.instance_variable_set(:@properties, "not properties") + paragraph.properties = "not properties" expect(validator.valid?(paragraph)).to be false end @@ -98,7 +98,7 @@ it "returns property error for invalid properties" do paragraph = Uniword::Wordprocessingml::Paragraph.new - paragraph.instance_variable_set(:@properties, "invalid") + paragraph.properties = "invalid" errors = validator.errors(paragraph) expect(errors).to include("Properties must be a ParagraphProperties instance") @@ -107,7 +107,7 @@ it "returns multiple errors for multiple issues" do paragraph = Uniword::Wordprocessingml::Paragraph.new paragraph.runs << "invalid run" - paragraph.instance_variable_set(:@properties, "invalid properties") + paragraph.properties = "invalid properties" errors = validator.errors(paragraph) expect(errors.size).to eq(2) @@ -135,7 +135,7 @@ describe "edge cases" do it "handles paragraph with nil runs array" do paragraph = Uniword::Wordprocessingml::Paragraph.new - paragraph.instance_variable_set(:@runs, nil) + paragraph.runs = nil expect(validator.valid?(paragraph)).to be true end diff --git a/spec/uniword/validators/table_validator_spec.rb b/spec/uniword/validators/table_validator_spec.rb index 992bca93..4fd002b3 100644 --- a/spec/uniword/validators/table_validator_spec.rb +++ b/spec/uniword/validators/table_validator_spec.rb @@ -156,7 +156,7 @@ def create_validator_text_row(*texts) describe "edge cases" do it "handles table with nil rows array" do table = Uniword::Wordprocessingml::Table.new - table.instance_variable_set(:@rows, nil) + table.rows = nil expect(validator.errors(table)).to be_empty end diff --git a/spec/uniword/wordprocessingml/comment_range_spec.rb b/spec/uniword/wordprocessingml/comment_range_spec.rb index 8ee15dd5..e65481a7 100644 --- a/spec/uniword/wordprocessingml/comment_range_spec.rb +++ b/spec/uniword/wordprocessingml/comment_range_spec.rb @@ -89,7 +89,7 @@ it "raises error for invalid marker type" do range = described_class.new(comment_id: "1") - range.instance_variable_set(:@marker_type, :invalid) + range.marker_type = :invalid expect do range.xml_element_name end.to raise_error(ArgumentError, /Invalid marker type/) @@ -120,7 +120,7 @@ it "returns false without marker_type" do range = described_class.new(comment_id: "1") - range.instance_variable_set(:@marker_type, nil) + range.marker_type = nil expect(range).not_to be_valid end end diff --git a/spec/uniword/wordprocessingml/comment_spec.rb b/spec/uniword/wordprocessingml/comment_spec.rb index 873e9f48..1b2d9ee0 100644 --- a/spec/uniword/wordprocessingml/comment_spec.rb +++ b/spec/uniword/wordprocessingml/comment_spec.rb @@ -166,7 +166,7 @@ it "returns false without comment_id" do comment = described_class.new(author: "John") - comment.instance_variable_set(:@comment_id, nil) + comment.comment_id = nil expect(comment).not_to be_valid end end diff --git a/spec/uniword/wordprocessingml/comments_part_spec.rb b/spec/uniword/wordprocessingml/comments_part_spec.rb index f7b61434..ea061580 100644 --- a/spec/uniword/wordprocessingml/comments_part_spec.rb +++ b/spec/uniword/wordprocessingml/comments_part_spec.rb @@ -23,9 +23,9 @@ it "assigns sequential ID if not set" do comment1 = Uniword::Comment.new(author: "John") - comment1.instance_variable_set(:@comment_id, nil) + comment1.comment_id = nil comment2 = Uniword::Comment.new(author: "Jane") - comment2.instance_variable_set(:@comment_id, nil) + comment2.comment_id = nil comments_part.add_comment(comment1) comments_part.add_comment(comment2) @@ -165,7 +165,7 @@ it "excludes nil authors" do comment = Uniword::Comment.new(comment_id: "1") - comment.instance_variable_set(:@author, nil) + comment.author = nil comments_part.add_comment(comment) expect(comments_part.authors).to be_empty @@ -186,7 +186,7 @@ comments_part.clear comment = Uniword::Comment.new(author: "Jane") - comment.instance_variable_set(:@comment_id, nil) + comment.comment_id = nil comments_part.add_comment(comment) expect(comment.comment_id).to eq("1") diff --git a/spec/uniword/wordprocessingml/tracked_changes_spec.rb b/spec/uniword/wordprocessingml/tracked_changes_spec.rb index 56e4d1c1..b6f11f22 100644 --- a/spec/uniword/wordprocessingml/tracked_changes_spec.rb +++ b/spec/uniword/wordprocessingml/tracked_changes_spec.rb @@ -31,9 +31,9 @@ it "assigns sequential ID if not set" do rev1 = Uniword::Revision.new(author: "John") - rev1.instance_variable_set(:@revision_id, nil) + rev1.revision_id = nil rev2 = Uniword::Revision.new(author: "Jane") - rev2.instance_variable_set(:@revision_id, nil) + rev2.revision_id = nil tracked_changes.add_revision(rev1) tracked_changes.add_revision(rev2) @@ -273,7 +273,7 @@ it "excludes nil authors" do revision = Uniword::Revision.new(revision_id: "1") - revision.instance_variable_set(:@author, nil) + revision.author = nil tracked_changes.add_revision(revision) expect(tracked_changes.authors).to be_empty @@ -294,7 +294,7 @@ tracked_changes.clear revision = Uniword::Revision.new(author: "Jane") - revision.instance_variable_set(:@revision_id, nil) + revision.revision_id = nil tracked_changes.add_revision(revision) expect(revision.revision_id).to eq("1") From 24ed08ee843dba96965c1dbd3d9e0175801192a9 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Thu, 16 Jul 2026 20:26:49 +0800 Subject: [PATCH 3/5] refactor: replace doubles with real instances in element and accessibility specs TODO 02 (partial): Replaced 5 of 65 double() calls: - element_spec: RecordingVisitor class instead of double('visitor') - accessibility_rule_spec: real DocumentRoot/Paragraph instead of double('Document')/double('Element') Remaining 60 doubles are in larger files (word_css_spec 14, image_alt_text_rule_spec 12, math_equation/plurimath 7 each) that need per-test analysis. --- .../accessibility/accessibility_rule_spec.rb | 6 ++--- spec/uniword/element_spec.rb | 22 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/spec/uniword/accessibility/accessibility_rule_spec.rb b/spec/uniword/accessibility/accessibility_rule_spec.rb index ae40ef26..943d0e9a 100644 --- a/spec/uniword/accessibility/accessibility_rule_spec.rb +++ b/spec/uniword/accessibility/accessibility_rule_spec.rb @@ -76,7 +76,7 @@ def check(_document) subject(:base_rule) { described_class.new(config) } it "raises NotImplementedError" do - document = double("Document") + document = Uniword::Wordprocessingml::DocumentRoot.new expect { base_rule.check(document) }.to raise_error( NotImplementedError, /must implement #check/, @@ -86,14 +86,14 @@ def check(_document) context "when implemented by subclass" do it "can be called without error" do - document = double("Document") + document = Uniword::Wordprocessingml::DocumentRoot.new expect { rule.check(document) }.not_to raise_error end end end describe "#create_violation" do - let(:element) { double("Element") } + let(:element) { Uniword::Wordprocessingml::Paragraph.new } let(:violation_params) do { message: "Test violation", diff --git a/spec/uniword/element_spec.rb b/spec/uniword/element_spec.rb index 68a924ab..6e9709e0 100644 --- a/spec/uniword/element_spec.rb +++ b/spec/uniword/element_spec.rb @@ -61,12 +61,20 @@ def accept(visitor) end describe "#accept" do + let(:recording_visitor) do + Class.new do + def initialize; @visits = []; end + attr_reader :visits + def visit_test_element(el); @visits << [:test, el]; end + def visit_another_element(el); @visits << [:another, el]; end + def visit_element(el); @visits << [:default, el]; end + end.new + end + it "calls visitor method for the element" do element = test_element_class.new - visitor = double("visitor") - expect(visitor).to receive(:visit_test_element).with(element) - - element.accept(visitor) + element.accept(recording_visitor) + expect(recording_visitor.visits).to eq([[:test, element]]) end context "with default accept method" do @@ -76,10 +84,8 @@ def accept(visitor) it "calls visit_element on visitor" do element = default_class.new - visitor = double("visitor") - expect(visitor).to receive(:visit_element).with(element) - - element.accept(visitor) + element.accept(recording_visitor) + expect(recording_visitor.visits).to eq([[:default, element]]) end end end From d35e5150cab1f2c102d445a5c8e110ef71b684b4 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Thu, 16 Jul 2026 20:28:21 +0800 Subject: [PATCH 4/5] refactor: replace Document double with real DocumentRoot in accessibility spec TODO 02 (partial): 3 more doubles replaced: - accessibility_checker_spec: real DocumentRoot.new instead of double('Document') with stubbed images/tables/paragraphs Total doubles remaining: 59 --- spec/uniword/accessibility/accessibility_checker_spec.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/spec/uniword/accessibility/accessibility_checker_spec.rb b/spec/uniword/accessibility/accessibility_checker_spec.rb index 189bf88a..f20a8d80 100644 --- a/spec/uniword/accessibility/accessibility_checker_spec.rb +++ b/spec/uniword/accessibility/accessibility_checker_spec.rb @@ -60,13 +60,7 @@ describe "#check" do let(:checker) { described_class.new(profile: :wcag_2_1_aa) } - let(:document) { double("Document") } - - before do - # Mock document methods - allow(document).to receive_messages(images: [], tables: [], - paragraphs: []) - end + let(:document) { Uniword::Wordprocessingml::DocumentRoot.new } it "returns an AccessibilityReport" do report = checker.check(document) From 4c6ec7a8838195eba7a45dba703e56f7ce83a0e3 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Thu, 16 Jul 2026 20:30:04 +0800 Subject: [PATCH 5/5] refactor: replace doubles with real instances in accessibility_report_spec TODO 02 (partial): 4 more doubles replaced with real Drawing/Paragraph instances. Total fixed: 10 of 65. --- spec/uniword/accessibility/accessibility_report_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/uniword/accessibility/accessibility_report_spec.rb b/spec/uniword/accessibility/accessibility_report_spec.rb index 7c7855a9..da587a74 100644 --- a/spec/uniword/accessibility/accessibility_report_spec.rb +++ b/spec/uniword/accessibility/accessibility_report_spec.rb @@ -18,7 +18,7 @@ wcag_criterion: "1.1.1 Non-text Content", level: "A", message: "Image 1 missing alternative text", - element: double("Image"), + element: Uniword::Wordprocessingml::Drawing.new, severity: :error, suggestion: "Add descriptive alt text", ) @@ -29,7 +29,7 @@ wcag_criterion: "1.3.1 Info and Relationships", level: "A", message: "Heading hierarchy skip", - element: double("Paragraph"), + element: Uniword::Wordprocessingml::Paragraph.new, severity: :warning, suggestion: "Use sequential heading levels", ) @@ -40,7 +40,7 @@ wcag_criterion: "2.4.6 Headings and Labels", level: "AA", message: "Heading could be more descriptive", - element: double("Paragraph"), + element: Uniword::Wordprocessingml::Paragraph.new, severity: :info, suggestion: "Make headings more descriptive", ) @@ -195,7 +195,7 @@ wcag_criterion: "1.1.1", level: "A", message: "Image #{i + 1} issue", - element: double("Image"), + element: Uniword::Wordprocessingml::Drawing.new, severity: :error, suggestion: "Fix it", ),